1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 5 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1997-2009 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: xpath.c 272374 2008-12-31 11:17:49Z sebastian $ */
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 */
40 : static
41 : ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_xpath_construct, 0, 0, 1)
42 : ZEND_ARG_OBJ_INFO(0, doc, DOMDocument, 0)
43 : ZEND_END_ARG_INFO();
44 :
45 : static
46 : ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_xpath_register_ns, 0, 0, 2)
47 : ZEND_ARG_INFO(0, prefix)
48 : ZEND_ARG_INFO(0, uri)
49 : ZEND_END_ARG_INFO();
50 :
51 : static
52 : ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_xpath_query, 0, 0, 1)
53 : ZEND_ARG_INFO(0, expr)
54 : ZEND_ARG_OBJ_INFO(0, context, DOMNode, 0)
55 : ZEND_END_ARG_INFO();
56 :
57 : static
58 : ZEND_BEGIN_ARG_INFO_EX(arginfo_dom_xpath_evaluate, 0, 0, 1)
59 : ZEND_ARG_INFO(0, expr)
60 : ZEND_ARG_OBJ_INFO(0, context, DOMNode, 0)
61 : ZEND_END_ARG_INFO();
62 : /* }}} */
63 :
64 : zend_function_entry php_dom_xpath_class_functions[] = {
65 : PHP_ME(domxpath, __construct, arginfo_dom_xpath_construct, ZEND_ACC_PUBLIC)
66 : PHP_FALIAS(registerNamespace, dom_xpath_register_ns, arginfo_dom_xpath_register_ns)
67 : PHP_FALIAS(query, dom_xpath_query, arginfo_dom_xpath_query)
68 : PHP_FALIAS(evaluate, dom_xpath_evaluate, arginfo_dom_xpath_evaluate)
69 : {NULL, NULL, NULL}
70 : };
71 :
72 : /* {{{ proto void DOMXPath::__construct(DOMDocument doc); */
73 : PHP_METHOD(domxpath, __construct)
74 7 : {
75 : zval *id, *doc;
76 7 : xmlDocPtr docp = NULL;
77 : dom_object *docobj, *intern;
78 : xmlXPathContextPtr ctx, oldctx;
79 :
80 7 : php_set_error_handling(EH_THROW, dom_domexception_class_entry TSRMLS_CC);
81 7 : if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &id, dom_xpath_class_entry, &doc, dom_document_class_entry) == FAILURE) {
82 0 : php_std_error_handling();
83 0 : return;
84 : }
85 :
86 7 : php_std_error_handling();
87 7 : DOM_GET_OBJ(docp, doc, xmlDocPtr, docobj);
88 :
89 7 : ctx = xmlXPathNewContext(docp);
90 7 : if (ctx == NULL) {
91 0 : php_dom_throw_error(INVALID_STATE_ERR, 1 TSRMLS_CC);
92 0 : RETURN_FALSE;
93 : }
94 :
95 7 : intern = (dom_object *)zend_object_store_get_object(id TSRMLS_CC);
96 7 : if (intern != NULL) {
97 7 : oldctx = (xmlXPathContextPtr)intern->ptr;
98 7 : if (oldctx != NULL) {
99 0 : php_libxml_decrement_doc_ref((php_libxml_node_object *)intern TSRMLS_CC);
100 0 : xmlXPathFreeContext(oldctx);
101 : }
102 7 : intern->ptr = ctx;
103 7 : intern->document = docobj->document;
104 7 : php_libxml_increment_doc_ref((php_libxml_node_object *)intern, docp TSRMLS_CC);
105 : }
106 : }
107 : /* }}} end DOMXPath::__construct */
108 :
109 : /* {{{ document DOMDocument*/
110 : int dom_xpath_document_read(dom_object *obj, zval **retval TSRMLS_DC)
111 0 : {
112 0 : xmlDoc *docp = NULL;
113 : xmlXPathContextPtr ctx;
114 : int ret;
115 :
116 0 : ctx = (xmlXPathContextPtr) obj->ptr;
117 :
118 0 : if (ctx) {
119 0 : docp = (xmlDocPtr) ctx->doc;
120 : }
121 :
122 0 : ALLOC_ZVAL(*retval);
123 0 : if (NULL == (*retval = php_dom_create_object((xmlNodePtr) docp, &ret, NULL, *retval, obj TSRMLS_CC))) {
124 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot create required DOM object");
125 0 : return FAILURE;
126 : }
127 0 : return SUCCESS;
128 : }
129 :
130 : /* {{{ proto boolean dom_xpath_register_ns(string prefix, string uri); */
131 : PHP_FUNCTION(dom_xpath_register_ns)
132 0 : {
133 : zval *id;
134 : xmlXPathContextPtr ctxp;
135 : int prefix_len, ns_uri_len;
136 : dom_object *intern;
137 : unsigned char *prefix, *ns_uri;
138 :
139 0 : 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) {
140 0 : return;
141 : }
142 :
143 0 : intern = (dom_object *)zend_object_store_get_object(id TSRMLS_CC);
144 :
145 0 : ctxp = (xmlXPathContextPtr) intern->ptr;
146 0 : if (ctxp == NULL) {
147 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid XPath Context");
148 0 : RETURN_FALSE;
149 : }
150 :
151 0 : if (xmlXPathRegisterNs(ctxp, prefix, ns_uri) != 0) {
152 0 : RETURN_FALSE
153 : }
154 0 : RETURN_TRUE;
155 : }
156 :
157 : static void dom_xpath_iter(zval *baseobj, dom_object *intern)
158 7 : {
159 : dom_nnodemap_object *mapptr;
160 :
161 7 : mapptr = (dom_nnodemap_object *)intern->ptr;
162 7 : mapptr->baseobjptr = baseobj;
163 7 : mapptr->nodetype = DOM_NODESET;
164 :
165 7 : }
166 :
167 7 : static void php_xpath_eval(INTERNAL_FUNCTION_PARAMETERS, int type) {
168 7 : zval *id, *retval, *context = NULL;
169 : xmlXPathContextPtr ctxp;
170 7 : xmlNodePtr nodep = NULL;
171 : xmlXPathObjectPtr xpathobjp;
172 7 : int expr_len, ret, nsnbr = 0, xpath_type;
173 : dom_object *intern, *nodeobj;
174 : char *expr;
175 7 : xmlDoc *docp = NULL;
176 : xmlNsPtr *ns;
177 :
178 :
179 7 : if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|O", &id, dom_xpath_class_entry, &expr, &expr_len, &context, dom_node_class_entry) == FAILURE) {
180 0 : return;
181 : }
182 :
183 7 : intern = (dom_object *)zend_object_store_get_object(id TSRMLS_CC);
184 :
185 7 : ctxp = (xmlXPathContextPtr) intern->ptr;
186 7 : if (ctxp == NULL) {
187 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid XPath Context");
188 0 : RETURN_FALSE;
189 : }
190 :
191 7 : docp = (xmlDocPtr) ctxp->doc;
192 7 : if (docp == NULL) {
193 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid XPath Document Pointer");
194 0 : RETURN_FALSE;
195 : }
196 :
197 7 : if (context != NULL) {
198 1 : DOM_GET_OBJ(nodep, context, xmlNodePtr, nodeobj);
199 : }
200 :
201 7 : if (!nodep) {
202 6 : nodep = xmlDocGetRootElement(docp);
203 : }
204 :
205 7 : if (nodep && docp != nodep->doc) {
206 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Node From Wrong Document");
207 0 : RETURN_FALSE;
208 : }
209 :
210 7 : ctxp->node = nodep;
211 :
212 : /* Register namespaces in the node */
213 7 : ns = xmlGetNsList(docp, nodep);
214 :
215 7 : if (ns != NULL) {
216 9 : while (ns[nsnbr] != NULL)
217 3 : nsnbr++;
218 : }
219 :
220 :
221 7 : ctxp->namespaces = ns;
222 7 : ctxp->nsNr = nsnbr;
223 :
224 7 : xpathobjp = xmlXPathEvalExpression(expr, ctxp);
225 7 : ctxp->node = NULL;
226 :
227 7 : if (ns != NULL) {
228 3 : xmlFree(ns);
229 3 : ctxp->namespaces = NULL;
230 3 : ctxp->nsNr = 0;
231 : }
232 :
233 7 : if (! xpathobjp) {
234 0 : RETURN_FALSE;
235 : }
236 :
237 7 : if (type == PHP_DOM_XPATH_QUERY) {
238 7 : xpath_type = XPATH_NODESET;
239 : } else {
240 0 : xpath_type = xpathobjp->type;
241 : }
242 :
243 7 : switch (xpath_type) {
244 :
245 : case XPATH_NODESET:
246 : {
247 : int i;
248 : xmlNodeSetPtr nodesetp;
249 :
250 7 : MAKE_STD_ZVAL(retval);
251 7 : array_init(retval);
252 :
253 7 : if (xpathobjp->type == XPATH_NODESET && NULL != (nodesetp = xpathobjp->nodesetval)) {
254 :
255 13 : for (i = 0; i < nodesetp->nodeNr; i++) {
256 6 : xmlNodePtr node = nodesetp->nodeTab[i];
257 : zval *child;
258 :
259 6 : MAKE_STD_ZVAL(child);
260 :
261 6 : if (node->type == XML_NAMESPACE_DECL) {
262 : xmlNsPtr curns;
263 : xmlNodePtr nsparent;
264 :
265 0 : nsparent = node->_private;
266 0 : curns = xmlNewNs(NULL, node->name, NULL);
267 0 : if (node->children) {
268 0 : curns->prefix = xmlStrdup((char *) node->children);
269 : }
270 0 : if (node->children) {
271 0 : node = xmlNewDocNode(docp, NULL, (char *) node->children, node->name);
272 : } else {
273 0 : node = xmlNewDocNode(docp, NULL, "xmlns", node->name);
274 : }
275 0 : node->type = XML_NAMESPACE_DECL;
276 0 : node->parent = nsparent;
277 0 : node->ns = curns;
278 : }
279 6 : child = php_dom_create_object(node, &ret, NULL, child, intern TSRMLS_CC);
280 6 : add_next_index_zval(retval, child);
281 : }
282 : }
283 7 : php_dom_create_interator(return_value, DOM_NODELIST TSRMLS_CC);
284 7 : intern = (dom_object *)zend_objects_get_address(return_value TSRMLS_CC);
285 7 : dom_xpath_iter(retval, intern);
286 7 : break;
287 : }
288 :
289 : case XPATH_BOOLEAN:
290 0 : RETVAL_BOOL(xpathobjp->boolval);
291 0 : break;
292 :
293 : case XPATH_NUMBER:
294 0 : RETVAL_DOUBLE(xpathobjp->floatval)
295 0 : break;
296 :
297 : case XPATH_STRING:
298 0 : RETVAL_STRING(xpathobjp->stringval, 1);
299 0 : break;
300 :
301 : default:
302 0 : RETVAL_NULL();
303 : break;
304 : }
305 :
306 7 : xmlXPathFreeObject(xpathobjp);
307 : }
308 :
309 : /* {{{ proto DOMNodeList dom_xpath_query(string expr [,DOMNode context]); */
310 : PHP_FUNCTION(dom_xpath_query)
311 7 : {
312 7 : php_xpath_eval(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_DOM_XPATH_QUERY);
313 7 : }
314 : /* }}} end dom_xpath_query */
315 :
316 : /* {{{ proto mixed dom_xpath_evaluate(string expr [,DOMNode context]); */
317 : PHP_FUNCTION(dom_xpath_evaluate)
318 0 : {
319 0 : php_xpath_eval(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_DOM_XPATH_EVALUATE);
320 0 : }
321 : /* }}} end dom_xpath_evaluate */
322 :
323 : #endif /* LIBXML_XPATH_ENABLED */
324 :
325 : /* }}} */
326 : #endif
|