xref: /php-src/ext/dom/attr.c (revision ac039cf7)
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 
24 #if defined(HAVE_LIBXML) && defined(HAVE_DOM)
25 
26 #include "php_dom.h"
27 #include "dom_properties.h"
28 
29 /*
30 * class DOMAttr extends DOMNode
31 *
32 * URL: https://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-637646024
33 * Since:
34 */
35 
36 /* {{{ */
PHP_METHOD(DOMAttr,__construct)37 PHP_METHOD(DOMAttr, __construct)
38 {
39 	xmlAttrPtr nodep = NULL;
40 	xmlNodePtr oldnode = NULL;
41 	dom_object *intern;
42 	char *name, *value = NULL;
43 	size_t name_len, value_len, name_valid;
44 
45 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", &name, &name_len, &value, &value_len) == FAILURE) {
46 		RETURN_THROWS();
47 	}
48 
49 	intern = Z_DOMOBJ_P(ZEND_THIS);
50 
51 	name_valid = xmlValidateName(BAD_CAST name, 0);
52 	if (name_valid != 0) {
53 		php_dom_throw_error(INVALID_CHARACTER_ERR, true);
54 		RETURN_THROWS();
55 	}
56 
57 	nodep = xmlNewProp(NULL, BAD_CAST name, BAD_CAST value);
58 
59 	if (!nodep) {
60 		php_dom_throw_error(INVALID_STATE_ERR, true);
61 		RETURN_THROWS();
62 	}
63 
64 	oldnode = dom_object_get_node(intern);
65 	if (oldnode != NULL) {
66 		php_libxml_node_decrement_resource((php_libxml_node_object *)intern);
67 	}
68 	php_libxml_increment_node_ptr((php_libxml_node_object *)intern, (xmlNodePtr)nodep, (void *)intern);
69 }
70 
71 /* }}} end DOMAttr::__construct */
72 
73 /* {{{ name	string
74 readonly=yes
75 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-1112119403
76 Modern spec URL: https://dom.spec.whatwg.org/#dom-attr-name
77 Since:
78 */
dom_attr_name_read(dom_object * obj,zval * retval)79 zend_result dom_attr_name_read(dom_object *obj, zval *retval)
80 {
81 	DOM_PROP_NODE(xmlAttrPtr, attrp, obj);
82 
83 	if (php_dom_follow_spec_intern(obj)) {
84 		zend_string *str = dom_node_get_node_name_attribute_or_element((xmlNodePtr) attrp, false);
85 		ZVAL_NEW_STR(retval, str);
86 	} else {
87 		ZVAL_STRING(retval, (char *) attrp->name);
88 	}
89 
90 	return SUCCESS;
91 }
92 
93 /* }}} */
94 
95 /* {{{ specified	boolean
96 readonly=yes
97 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-862529273
98 Since:
99 */
dom_attr_specified_read(dom_object * obj,zval * retval)100 zend_result dom_attr_specified_read(dom_object *obj, zval *retval)
101 {
102 	/* From spec: "useless; always returns true" */
103 	ZVAL_TRUE(retval);
104 	return SUCCESS;
105 }
106 
107 /* }}} */
108 
109 /* {{{ value	string
110 readonly=no
111 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-221662474
112 Since:
113 */
dom_attr_value_read(dom_object * obj,zval * retval)114 zend_result dom_attr_value_read(dom_object *obj, zval *retval)
115 {
116 	DOM_PROP_NODE(xmlNodePtr, attrp, obj);
117 	php_dom_get_content_into_zval(attrp, retval, false);
118 	return SUCCESS;
119 }
120 
dom_attr_value_write(dom_object * obj,zval * newval)121 zend_result dom_attr_value_write(dom_object *obj, zval *newval)
122 {
123 	DOM_PROP_NODE(xmlAttrPtr, attrp, obj);
124 
125 	/* Typed property, this is already a string */
126 	ZEND_ASSERT(Z_TYPE_P(newval) == IS_STRING);
127 	zend_string *str = Z_STR_P(newval);
128 
129 	dom_remove_all_children((xmlNodePtr) attrp);
130 
131 	if (php_dom_follow_spec_intern(obj)) {
132 		xmlNodePtr node = xmlNewDocTextLen(attrp->doc, BAD_CAST ZSTR_VAL(str), ZSTR_LEN(str));
133 		xmlAddChild((xmlNodePtr) attrp, node);
134 	} else {
135 		xmlNodeSetContentLen((xmlNodePtr) attrp, BAD_CAST ZSTR_VAL(str), ZSTR_LEN(str));
136 	}
137 
138 	return SUCCESS;
139 }
140 
141 /* }}} */
142 
143 /* {{{ ownerElement	DOMElement
144 readonly=yes
145 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Attr-ownerElement
146 Since: DOM Level 2
147 */
dom_attr_owner_element_read(dom_object * obj,zval * retval)148 zend_result dom_attr_owner_element_read(dom_object *obj, zval *retval)
149 {
150 	DOM_PROP_NODE(xmlNodePtr, nodep, obj);
151 
152 	xmlNodePtr nodeparent = nodep->parent;
153 	if (!nodeparent) {
154 		ZVAL_NULL(retval);
155 		return SUCCESS;
156 	}
157 
158 	php_dom_create_object(nodeparent, retval, obj);
159 	return SUCCESS;
160 }
161 
162 /* }}} */
163 
164 /* {{{ schemaTypeInfo	DOMTypeInfo
165 readonly=yes
166 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Attr-schemaTypeInfo
167 Since: DOM Level 3
168 */
dom_attr_schema_type_info_read(dom_object * obj,zval * retval)169 zend_result dom_attr_schema_type_info_read(dom_object *obj, zval *retval)
170 {
171 	/* TODO */
172 	ZVAL_NULL(retval);
173 	return SUCCESS;
174 }
175 
176 /* }}} */
177 
178 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Attr-isId
179 Since: DOM Level 3
180 */
PHP_METHOD(DOMAttr,isId)181 PHP_METHOD(DOMAttr, isId)
182 {
183 	zval *id;
184 	dom_object *intern;
185 	xmlAttrPtr attrp;
186 
187 	id = ZEND_THIS;
188 	if (zend_parse_parameters_none() == FAILURE) {
189 		RETURN_THROWS();
190 	}
191 
192 	DOM_GET_OBJ(attrp, id, xmlAttrPtr, intern);
193 
194 	if (attrp->atype == XML_ATTRIBUTE_ID) {
195 		RETURN_TRUE;
196 	} else {
197 		RETURN_FALSE;
198 	}
199 }
200 /* }}} end dom_attr_is_id */
201 
dom_attr_value(const xmlAttr * attr,bool * free)202 xmlChar *dom_attr_value(const xmlAttr *attr, bool *free)
203 {
204 	/* For attributes we can have an optimized fast-path.
205 	 * This fast-path is only possible in the (common) case where the attribute
206 	 * has a single text child. Note that if the child or the content is NULL, this
207 	 * is equivalent to not having content (i.e. the attribute has the empty string as value). */
208 
209 	*free = false;
210 
211 	if (attr->children == NULL) {
212 		return BAD_CAST "";
213 	}
214 
215 	if (attr->children->type == XML_TEXT_NODE && attr->children->next == NULL) {
216 		if (attr->children->content == NULL) {
217 			return BAD_CAST "";
218 		} else {
219 			return attr->children->content;
220 		}
221 	}
222 
223 	xmlChar *value = xmlNodeGetContent((const xmlNode *) attr);
224 	if (UNEXPECTED(value == NULL)) {
225 		return BAD_CAST "";
226 	}
227 
228 	*free = true;
229 	return value;
230 }
231 
dom_compare_value(const xmlAttr * attr,const xmlChar * value)232 bool dom_compare_value(const xmlAttr *attr, const xmlChar *value)
233 {
234 	bool free;
235 	xmlChar *attr_value = dom_attr_value(attr, &free);
236 	bool result = xmlStrEqual(attr_value, value);
237 	if (free) {
238 		xmlFree(attr_value);
239 	}
240 	return result;
241 }
242 
243 #endif
244