hphp/runtime/ext/simplexml/ext_simplexml.cpp (1,620 lines of code) (raw):
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
| Copyright (c) 1997-2010 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include "hphp/runtime/base/array-init.h"
#include "hphp/runtime/ext/simplexml/ext_simplexml.h"
#include <vector>
#include "hphp/runtime/base/builtin-functions.h"
#include "hphp/runtime/base/file.h"
#include "hphp/runtime/ext/extension.h"
#include "hphp/runtime/ext/simplexml/ext_simplexml_include.h"
#include "hphp/runtime/ext/domdocument/ext_domdocument.h"
#include "hphp/runtime/ext/std/ext_std_file.h"
#include "hphp/runtime/ext/libxml/ext_libxml.h"
#include "hphp/system/systemlib.h"
#include "hphp/runtime/vm/vm-regs.h"
#include "hphp/runtime/vm/native-prop-handler.h"
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
// Iter type
enum SXE_ITER {
SXE_ITER_NONE = 0,
SXE_ITER_ELEMENT = 1,
SXE_ITER_CHILD = 2,
SXE_ITER_ATTRLIST = 3
};
const StaticString
s_SimpleXMLElement("SimpleXMLElement"),
s_SimpleXMLElementIterator("SimpleXMLElementIterator"),
s_SimpleXMLIterator("SimpleXMLIterator");
const Class* SimpleXMLElement_classof() {
static auto cls = Class::lookup(s_SimpleXMLElement.get());
return cls;
}
const Class* SimpleXMLElementIterator_classof() {
static auto cls = Class::lookup(s_SimpleXMLElementIterator.get());
return cls;
}
const Class* SimpleXMLIterator_classof() {
static auto cls = Class::lookup(s_SimpleXMLIterator.get());
return cls;
}
///////////////////////////////////////////////////////////////////////////////
// NativeData definitions
struct SimpleXMLElement {
SimpleXMLElement() {
assertx(Native::object<SimpleXMLElement>(this)->getVMClass()->
rtAttribute(Class::CallToImpl));
}
SimpleXMLElement& operator=(const SimpleXMLElement &src) {
iter.isprefix = src.iter.isprefix;
if (src.iter.name != nullptr) {
iter.name = xmlStrdup((xmlChar*)src.iter.name);
}
if (src.iter.nsprefix != nullptr) {
iter.nsprefix = xmlStrdup((xmlChar*)src.iter.nsprefix);
}
iter.type = src.iter.type;
if (src.nodep()) {
node = libxml_register_node(
xmlDocCopyNode(src.nodep(), src.docp(), 1)
);
}
return *this;
}
~SimpleXMLElement() { sweep(); }
void sweep() {
if (iter.name) {
xmlFree(iter.name);
}
if (iter.nsprefix) {
xmlFree(iter.nsprefix);
}
if (xpath) {
xmlXPathFreeContext(xpath);
}
}
xmlNodePtr nodep() const {
return node ? node->nodep() : nullptr;
}
xmlDocPtr docp() const {
return node ? node->docp() : nullptr;
}
XMLNode node{nullptr};
xmlXPathContextPtr xpath{nullptr};
struct {
xmlChar* name{nullptr};
xmlChar* nsprefix{nullptr};
bool isprefix{false};
SXE_ITER type{SXE_ITER_NONE};
Object data;
} iter;
};
struct SimpleXMLElementIterator {
SimpleXMLElement* sxe() {
assertx(m_sxe->instanceof(SimpleXMLElement_classof()));
return Native::data<SimpleXMLElement>(m_sxe.get());
}
void setSxe(const Object& sxe) {
assertx(sxe->instanceof(SimpleXMLElement_classof()));
m_sxe = Object(sxe.get());
}
private:
Object m_sxe;
};
using SimpleXMLIterator = SimpleXMLElement;
///////////////////////////////////////////////////////////////////////////////
// Helpers
#define SKIP_TEXT(__p) \
if ((__p)->type == XML_TEXT_NODE) { \
goto next_iter; \
}
#define SXE_NS_PREFIX(ns) (ns->prefix ? (char*)ns->prefix : "")
static inline void sxe_add_namespace_name(Array& ret, xmlNsPtr ns) {
String prefix = String(SXE_NS_PREFIX(ns));
if (!ret.exists(prefix)) {
ret.set(prefix, String((char*)ns->href, CopyString));
}
}
static void sxe_add_registered_namespaces(SimpleXMLElement* sxe,
xmlNodePtr node, bool recursive,
Array& return_value) {
if (node != nullptr && node->type == XML_ELEMENT_NODE) {
xmlNsPtr ns = node->nsDef;
while (ns != nullptr) {
sxe_add_namespace_name(return_value, ns);
ns = ns->next;
}
if (recursive) {
node = node->children;
while (node) {
sxe_add_registered_namespaces(sxe, node, recursive, return_value);
node = node->next;
}
}
}
}
static void sxe_add_namespaces(SimpleXMLElement* sxe, xmlNodePtr node,
bool recursive, Array& return_value) {
if (node->ns) {
sxe_add_namespace_name(return_value, node->ns);
}
xmlAttrPtr attr = node->properties;
while (attr) {
if (attr->ns) {
sxe_add_namespace_name(return_value, attr->ns);
}
attr = attr->next;
}
if (recursive) {
node = node->children;
while (node) {
if (node->type == XML_ELEMENT_NODE) {
sxe_add_namespaces(sxe, node, recursive, return_value);
}
node = node->next;
}
}
}
static Object _node_as_zval(SimpleXMLElement* sxe, xmlNodePtr node,
SXE_ITER itertype, const char* name,
const xmlChar* nsprefix, bool isprefix) {
auto sxeObj = Native::object<SimpleXMLElement>(sxe);
Object obj = create_object(sxeObj->getClassName(), Array(), false);
auto subnode = Native::data<SimpleXMLElement>(obj.get());
subnode->iter.type = itertype;
if (name) {
subnode->iter.name = xmlStrdup((xmlChar*)name);
}
if (nsprefix && *nsprefix) {
subnode->iter.nsprefix = xmlStrdup(nsprefix);
subnode->iter.isprefix = isprefix;
}
subnode->node = libxml_register_node(node);
return obj;
}
static inline bool match_ns(SimpleXMLElement* /*sxe*/, xmlNodePtr node,
xmlChar* name, bool prefix) {
if (name == nullptr && (node->ns == nullptr || node->ns->prefix == nullptr)) {
return true;
}
if (RuntimeOption::SimpleXMLEmptyNamespaceMatchesAll &&
(name == nullptr || *name == '\0')) {
return true;
}
if (node->ns &&
!xmlStrcmp(prefix ? node->ns->prefix : node->ns->href, name)) {
return true;
}
return false;
}
static xmlNodePtr sxe_get_element_by_offset(SimpleXMLElement* sxe,
long offset, xmlNodePtr node,
long* cnt) {
if (sxe->iter.type == SXE_ITER_NONE) {
if (offset == 0) {
if (cnt) {
*cnt = 0;
}
return node;
} else {
return nullptr;
}
}
long nodendx = 0;
while (node && nodendx <= offset) {
SKIP_TEXT(node)
if (node->type == XML_ELEMENT_NODE &&
match_ns(sxe, node, sxe->iter.nsprefix, sxe->iter.isprefix)) {
if (sxe->iter.type == SXE_ITER_CHILD ||
(sxe->iter.type == SXE_ITER_ELEMENT
&& !xmlStrcmp(node->name, sxe->iter.name))) {
if (nodendx == offset) {
break;
}
nodendx++;
}
}
next_iter:
node = node->next;
}
if (cnt) {
*cnt = nodendx;
}
return node;
}
static xmlNodePtr php_sxe_iterator_fetch(SimpleXMLElement* sxe,
xmlNodePtr node, int use_data) {
xmlChar* prefix = sxe->iter.nsprefix;
bool isprefix = sxe->iter.isprefix;
bool test_elem = sxe->iter.type == SXE_ITER_ELEMENT && sxe->iter.name;
bool test_attr = sxe->iter.type == SXE_ITER_ATTRLIST && sxe->iter.name;
while (node) {
SKIP_TEXT(node)
if (sxe->iter.type != SXE_ITER_ATTRLIST && node->type == XML_ELEMENT_NODE) {
if ((!test_elem || !xmlStrcmp(node->name, sxe->iter.name))
&& match_ns(sxe, node, prefix, isprefix)) {
break;
}
} else if (node->type == XML_ATTRIBUTE_NODE) {
if ((!test_attr || !xmlStrcmp(node->name, sxe->iter.name)) &&
match_ns(sxe, node, prefix, isprefix)) {
break;
}
}
next_iter:
node = node->next;
}
if (node && use_data) {
sxe->iter.data = _node_as_zval(sxe, node, SXE_ITER_NONE, nullptr, prefix,
isprefix);
}
return node;
}
static void php_sxe_move_forward_iterator(SimpleXMLElement* sxe) {
xmlNodePtr node = nullptr;
auto data = sxe->iter.data;
if (!data.isNull()) {
assertx(data->instanceof(SimpleXMLElement_classof()));
auto intern = Native::data<SimpleXMLElement>(data.get());
node = intern->nodep();
sxe->iter.data.reset();
}
if (node) {
php_sxe_iterator_fetch(sxe, node->next, 1);
}
}
static xmlNodePtr php_sxe_reset_iterator(SimpleXMLElement* sxe,
bool use_data) {
if (!sxe->iter.data.isNull()) {
sxe->iter.data.reset();
}
xmlNodePtr node = sxe->nodep();
if (node) {
switch (sxe->iter.type) {
case SXE_ITER_ELEMENT:
case SXE_ITER_CHILD:
case SXE_ITER_NONE:
node = node->children;
break;
case SXE_ITER_ATTRLIST:
node = (xmlNodePtr)node->properties;
}
return php_sxe_iterator_fetch(sxe, node, use_data);
}
return nullptr;
}
static int64_t php_sxe_count_elements_helper(SimpleXMLElement* sxe) {
Object data = sxe->iter.data;
sxe->iter.data.reset();
xmlNodePtr node = php_sxe_reset_iterator(sxe, false);
int64_t count = 0;
while (node) {
count++;
node = php_sxe_iterator_fetch(sxe, node->next, 0);
}
sxe->iter.data = data;
return count;
}
static xmlNodePtr php_sxe_get_first_node(SimpleXMLElement* sxe,
xmlNodePtr node) {
if (sxe && sxe->iter.type != SXE_ITER_NONE) {
php_sxe_reset_iterator(sxe, true);
xmlNodePtr retnode = nullptr;
if (!sxe->iter.data.isNull()) {
assertx(sxe->iter.data->instanceof(SimpleXMLElement_classof()));
retnode = Native::data<SimpleXMLElement>(sxe->iter.data.get())->nodep();
}
return retnode;
} else {
return node;
}
}
xmlNodePtr SimpleXMLElement_exportNode(const Object& sxe) {
if (!sxe->instanceof(SimpleXMLElement_classof())) return nullptr;
auto data = Native::data<SimpleXMLElement>(sxe.get());
return php_sxe_get_first_node(data, data->nodep());
}
static Object sxe_prop_dim_read(SimpleXMLElement* sxe, const Variant& member,
bool elements, bool attribs) {
xmlNodePtr node = sxe->nodep();
String name = "";
if (member.isNull() || member.isInteger()) {
if (sxe->iter.type != SXE_ITER_ATTRLIST) {
attribs = false;
elements = true;
} else if (member.isNull()) {
/* This happens when the user did: $sxe[]->foo = $value */
raise_error("Cannot create unnamed attribute");
return Object{};
}
} else {
name = member.toString();
}
xmlAttrPtr attr = nullptr;
bool test = false;
if (sxe->iter.type == SXE_ITER_ATTRLIST) {
attribs = true;
elements = false;
node = php_sxe_get_first_node(sxe, node);
attr = (xmlAttrPtr)node;
test = sxe->iter.name != nullptr;
} else if (sxe->iter.type != SXE_ITER_CHILD) {
node = php_sxe_get_first_node(sxe, node);
attr = node ? node->properties : nullptr;
test = false;
if (member.isNull() && node && node->parent &&
node->parent->type == XML_DOCUMENT_NODE) {
/* This happens when the user did: $sxe[]->foo = $value */
raise_error("Cannot create unnamed attribute");
return Object{};
}
}
Object return_value;
if (node) {
if (attribs) {
if (!member.isInteger() || sxe->iter.type == SXE_ITER_ATTRLIST) {
if (member.isInteger()) {
int64_t nodendx = 0;
while (attr && nodendx <= member.toInt64()) {
if ((!test || !xmlStrcmp(attr->name, sxe->iter.name)) &&
match_ns(sxe, (xmlNodePtr) attr, sxe->iter.nsprefix,
sxe->iter.isprefix)) {
if (nodendx == member.toInt64()) {
return_value = _node_as_zval(sxe, (xmlNodePtr) attr,
SXE_ITER_NONE, nullptr,
sxe->iter.nsprefix,
sxe->iter.isprefix);
break;
}
nodendx++;
}
attr = attr->next;
}
} else {
while (attr) {
if ((!test || !xmlStrcmp(attr->name, sxe->iter.name)) &&
!xmlStrcmp(attr->name, (xmlChar*)name.data()) &&
match_ns(sxe, (xmlNodePtr) attr, sxe->iter.nsprefix,
sxe->iter.isprefix)) {
return_value = _node_as_zval(sxe, (xmlNodePtr) attr,
SXE_ITER_NONE,
nullptr,
sxe->iter.nsprefix,
sxe->iter.isprefix);
break;
}
attr = attr->next;
}
}
}
}
if (elements) {
if (!sxe->nodep()) {
sxe->node = libxml_register_node(node);
}
if (member.isNull() || member.isInteger()) {
long cnt = 0;
xmlNodePtr mynode = node;
if (sxe->iter.type == SXE_ITER_CHILD) {
node = php_sxe_get_first_node(sxe, node);
}
if (sxe->iter.type == SXE_ITER_NONE) {
if (!member.isNull() && member.toInt64() > 0) {
raise_warning("Cannot add element %s number %" PRId64 " when "
"only 0 such elements exist", mynode->name,
member.toInt64());
}
} else if (!member.isNull()) {
node = sxe_get_element_by_offset(sxe, member.toInt64(), node, &cnt);
} else {
node = nullptr;
}
if (node) {
return_value = _node_as_zval(sxe, node, SXE_ITER_NONE, nullptr,
sxe->iter.nsprefix, sxe->iter.isprefix);
}
// Zend would check here if this is a write operation, but HHVM always
// handles that with offsetSet so we just want to return nullptr here.
} else {
#if SXE_ELEMENT_BY_NAME
int newtype;
node = sxe->nodep();
node = sxe_get_element_by_name(sxe, node, &name.data(), &newtype);
if (node) {
return_value = _node_as_zval(sxe, node, newtype, name.data(),
sxe->iter.nsprefix, sxe->iter.isprefix);
}
#else
return_value = _node_as_zval(sxe, node, SXE_ITER_ELEMENT, name.data(),
sxe->iter.nsprefix, sxe->iter.isprefix);
#endif
}
}
}
return return_value;
}
static void change_node_zval(xmlNodePtr node, const Variant& value) {
if (value.isNull()) {
xmlNodeSetContentLen(node, (xmlChar*)"", 0);
return;
}
if (value.isInteger() || value.isBoolean() || value.isDouble() ||
value.isNull() || value.isString()) {
xmlChar* buffer =
xmlEncodeEntitiesReentrant(node->doc,
(xmlChar*)value.toString().data());
int64_t buffer_len = xmlStrlen(buffer);
/* check for nullptr buffer in case of
* memory error in xmlEncodeEntitiesReentrant */
if (buffer) {
xmlNodeSetContentLen(node, buffer, buffer_len);
xmlFree(buffer);
}
} else {
raise_warning("It is not possible to assign complex types to nodes");
}
}
static void sxe_prop_dim_delete(SimpleXMLElement* sxe, const Variant& member,
bool elements, bool attribs) {
xmlNodePtr node = sxe->nodep();
if (member.isInteger()) {
if (sxe->iter.type != SXE_ITER_ATTRLIST) {
attribs = false;
elements = true;
if (sxe->iter.type == SXE_ITER_CHILD) {
node = php_sxe_get_first_node(sxe, node);
}
}
}
xmlAttrPtr attr = nullptr;
bool test = 0;
if (sxe->iter.type == SXE_ITER_ATTRLIST) {
attribs = true;
elements = false;
node = php_sxe_get_first_node(sxe, node);
attr = (xmlAttrPtr)node;
test = sxe->iter.name != nullptr;
} else if (sxe->iter.type != SXE_ITER_CHILD) {
node = php_sxe_get_first_node(sxe, node);
attr = node ? node->properties : nullptr;
test = false;
}
if (node) {
if (attribs) {
if (member.isInteger()) {
int64_t nodendx = 0;
while (attr && nodendx <= member.toInt64()) {
if ((!test || !xmlStrcmp(attr->name, sxe->iter.name)) &&
match_ns(sxe, (xmlNodePtr) attr, sxe->iter.nsprefix,
sxe->iter.isprefix)) {
if (nodendx == member.toInt64()) {
libxml_register_node((xmlNodePtr) attr)->unlink();
break;
}
nodendx++;
}
attr = attr->next;
}
} else {
xmlAttrPtr anext;
while (attr) {
anext = attr->next;
if ((!test || !xmlStrcmp(attr->name, sxe->iter.name)) &&
!xmlStrcmp(attr->name, (xmlChar*)member.toString().data()) &&
match_ns(sxe, (xmlNodePtr) attr, sxe->iter.nsprefix,
sxe->iter.isprefix)) {
libxml_register_node((xmlNodePtr) attr)->unlink();
break;
}
attr = anext;
}
}
}
if (elements) {
if (member.isInteger()) {
if (sxe->iter.type == SXE_ITER_CHILD) {
node = php_sxe_get_first_node(sxe, node);
}
node = sxe_get_element_by_offset(sxe, member.toInt64(), node, nullptr);
if (node) {
libxml_register_node(node)->unlink();
}
} else {
node = node->children;
xmlNodePtr nnext;
while (node) {
nnext = node->next;
SKIP_TEXT(node);
if (!xmlStrcmp(node->name, (xmlChar*)member.toString().data())) {
libxml_register_node(node)->unlink();
}
next_iter:
node = nnext;
}
}
}
}
}
static bool sxe_prop_dim_exists(SimpleXMLElement* sxe, const Variant& member,
bool check_empty, bool elements, bool attribs) {
xmlNodePtr node = sxe->nodep();
if (member.isInteger()) {
if (sxe->iter.type != SXE_ITER_ATTRLIST) {
attribs = false;
elements = true;
if (sxe->iter.type == SXE_ITER_CHILD) {
node = php_sxe_get_first_node(sxe, node);
}
}
}
xmlAttrPtr attr = nullptr;
bool test = false;
if (sxe->iter.type == SXE_ITER_ATTRLIST) {
attribs = true;
elements = false;
node = php_sxe_get_first_node(sxe, node);
attr = (xmlAttrPtr)node;
test = sxe->iter.name != nullptr;
} else if (sxe->iter.type != SXE_ITER_CHILD) {
node = php_sxe_get_first_node(sxe, node);
attr = node ? node->properties : nullptr;
test = false;
}
bool exists = false;
if (node) {
if (attribs) {
if (member.isInteger()) {
int64_t nodendx = 0;
while (attr && nodendx <= member.toInt64()) {
if ((!test || !xmlStrcmp(attr->name, sxe->iter.name)) &&
match_ns(sxe, (xmlNodePtr) attr, sxe->iter.nsprefix,
sxe->iter.isprefix)) {
if (nodendx == member.toInt64()) {
exists = true;
break;
}
nodendx++;
}
attr = attr->next;
}
} else {
while (attr) {
if ((!test || !xmlStrcmp(attr->name, sxe->iter.name)) &&
!xmlStrcmp(attr->name, (xmlChar*)member.toString().data()) &&
match_ns(sxe, (xmlNodePtr) attr, sxe->iter.nsprefix,
sxe->iter.isprefix)) {
exists = true;
break;
}
attr = attr->next;
}
}
if (exists && check_empty == 1 &&
(!attr->children || !attr->children->content ||
!attr->children->content[0] ||
!xmlStrcmp(attr->children->content, (const xmlChar*)"0")) ) {
/* Attribute with no content in it's text node */
exists = false;
}
}
if (elements) {
if (member.isInteger()) {
if (sxe->iter.type == SXE_ITER_CHILD) {
node = php_sxe_get_first_node(sxe, node);
}
node = sxe_get_element_by_offset(sxe, member.toInt64(), node, nullptr);
}
else {
node = node->children;
while (node) {
xmlNodePtr nnext;
nnext = node->next;
if ((node->type == XML_ELEMENT_NODE) &&
!xmlStrcmp(node->name, (xmlChar*)member.toString().data())) {
break;
}
node = nnext;
}
}
if (node) {
exists = true;
if (check_empty == true &&
(!node->children || (node->children->type == XML_TEXT_NODE &&
!node->children->next &&
(!node->children->content ||
!node->children->content[0] ||
!xmlStrcmp(node->children->content,
(const xmlChar*)"0"))))) {
exists = false;
}
}
}
}
return exists;
}
static inline String sxe_xmlNodeListGetString(xmlDocPtr doc, xmlNodePtr list,
bool inLine) {
xmlChar* tmp = xmlNodeListGetString(doc, list, inLine);
if (tmp) {
String ret = String((char*)tmp);
xmlFree(tmp);
return ret;
} else {
return empty_string();
}
}
static Variant _get_base_node_value(SimpleXMLElement* sxe_ref,
xmlNodePtr node, xmlChar* nsprefix,
bool isprefix) {
if (node->children &&
node->children->type == XML_TEXT_NODE &&
!xmlIsBlankNode(node->children)) {
xmlChar* contents = xmlNodeListGetString(node->doc, node->children, 1);
if (contents) {
String obj = String((char*)contents);
xmlFree(contents);
return obj;
}
} else {
auto sxeRefObj = Native::object<SimpleXMLElement>(sxe_ref);
Object obj = create_object(sxeRefObj->getClassName(), Array(), false);
auto subnode = Native::data<SimpleXMLElement>(obj.get());
if (nsprefix && *nsprefix) {
subnode->iter.nsprefix = xmlStrdup((xmlChar*)nsprefix);
subnode->iter.isprefix = isprefix;
}
subnode->node = libxml_register_node(node);
return obj;
}
return init_null();
}
static void sxe_properties_add(Array& rv, char* name, const Variant& value) {
String sName = String(name);
if (rv.exists(sName)) {
Variant existVal = rv[sName];
if (existVal.isArray()) {
Array arr = existVal.toArray();
arr.append(value);
rv.set(sName, arr);
} else {
Array arr = make_vec_array(existVal, value);
rv.set(sName, arr);
}
} else {
rv.set(sName, value);
}
}
const static StaticString s_atAttributes{"@attributes"};
static void sxe_get_prop_hash(SimpleXMLElement* sxe, bool is_debug,
Array& rv, bool isBoolCast = false) {
rv.clear();
Object iter_data;
bool use_iter = false;
xmlNodePtr node = sxe->nodep();
if (!node) {
return;
}
if (is_debug || sxe->iter.type != SXE_ITER_CHILD) {
if (sxe->iter.type == SXE_ITER_ELEMENT) {
node = php_sxe_get_first_node(sxe, node);
}
if (!node || node->type != XML_ENTITY_DECL) {
xmlAttrPtr attr = node ? (xmlAttrPtr)node->properties : nullptr;
Array zattr = Array::CreateDict();
bool test = sxe->iter.name && sxe->iter.type == SXE_ITER_ATTRLIST;
while (attr) {
if ((!test || !xmlStrcmp(attr->name, sxe->iter.name)) &&
match_ns(sxe, (xmlNodePtr)attr, sxe->iter.nsprefix,
sxe->iter.isprefix)) {
zattr.set(String((char*)attr->name),
sxe_xmlNodeListGetString(
sxe->docp(),
attr->children,
1));
}
attr = attr->next;
}
if (zattr.size()) {
rv.set(s_atAttributes, zattr);
}
}
}
node = sxe->nodep();
node = php_sxe_get_first_node(sxe, node);
if (node && sxe->iter.type != SXE_ITER_ATTRLIST) {
if (node->type == XML_ATTRIBUTE_NODE) {
rv.append(sxe_xmlNodeListGetString(node->doc, node->children, 1));
node = nullptr;
} else if (sxe->iter.type != SXE_ITER_CHILD) {
if (sxe->iter.type == SXE_ITER_NONE || !node->children ||
!node->parent ||
node->children->next ||
node->children->children ||
node->parent->children == node->parent->last) {
node = node->children;
} else {
iter_data = sxe->iter.data;
sxe->iter.data.reset();
node = php_sxe_reset_iterator(sxe, false);
use_iter = true;
}
}
char *name = nullptr;
Variant value;
while (node) {
if (node->children != nullptr || node->prev != nullptr ||
node->next != nullptr) {
SKIP_TEXT(node);
} else {
if (node->type == XML_TEXT_NODE) {
const xmlChar* cur = node->content;
if (*cur != 0) {
rv.append(sxe_xmlNodeListGetString(node->doc, node, 1));
}
goto next_iter;
}
}
if (node->type == XML_ELEMENT_NODE &&
(!match_ns(sxe, node, sxe->iter.nsprefix, sxe->iter.isprefix))) {
goto next_iter;
}
name = (char*)node->name;
if (!name) {
goto next_iter;
}
value = _get_base_node_value(sxe, node, sxe->iter.nsprefix,
sxe->iter.isprefix);
if (use_iter) {
rv.append(value);
} else {
sxe_properties_add(rv, name, value);
}
if (isBoolCast) break;
next_iter:
if (use_iter) {
node = php_sxe_iterator_fetch(sxe, node->next, 0);
} else {
node = node->next;
}
}
}
if (use_iter) {
sxe->iter.data = iter_data;
}
}
Array SimpleXMLElement_darrayCast(const ObjectData* obj) {
auto sxe = Native::data<SimpleXMLElement>(const_cast<ObjectData*>(obj));
Array properties = Array::CreateDict();
sxe_get_prop_hash(sxe, true, properties);
return properties;
}
Variant SimpleXMLElement_objectCast(const ObjectData* obj, DataType type) {
assertx(!isArrayLikeType(type));
assertx(obj->instanceof(SimpleXMLElement_classof()));
auto sxe = Native::data<SimpleXMLElement>(const_cast<ObjectData*>(obj));
if (type == KindOfBoolean) {
xmlNodePtr node = php_sxe_get_first_node(sxe, nullptr);
if (node) return true;
Array properties = Array::CreateDict();
sxe_get_prop_hash(sxe, true, properties, true);
return properties.size() != 0;
}
xmlChar* contents = nullptr;
if (sxe->iter.type != SXE_ITER_NONE) {
xmlNodePtr node = php_sxe_get_first_node(sxe, nullptr);
if (node) {
contents = xmlNodeListGetString(sxe->docp(), node->children, 1);
}
} else {
xmlDocPtr doc = sxe->docp();
if (!sxe->nodep()) {
if (doc) {
sxe->node = libxml_register_node(xmlDocGetRootElement(doc));
}
}
if (sxe->nodep()) {
if (sxe->nodep()->children) {
contents = xmlNodeListGetString(doc, sxe->nodep()->children, 1);
}
}
}
String ret = String((char*)contents);
if (contents) {
xmlFree(contents);
}
switch (type) {
case KindOfString: return ret;
case KindOfInt64: return ret.toInt64();
case KindOfDouble: return ret.toDouble();
default: return init_null();
}
}
static bool sxe_prop_dim_write(SimpleXMLElement* sxe, const Variant& member,
const Variant& value, bool elements, bool attribs,
xmlNodePtr* pnewnode) {
xmlNodePtr node = sxe->nodep();
if (member.isNull() || member.isInteger()) {
if (sxe->iter.type != SXE_ITER_ATTRLIST) {
attribs = false;
elements = true;
} else if (member.isNull()) {
/* This happens when the user did: $sxe[] = $value
* and could also be E_PARSE, but we use this only during parsing
* and this is during runtime.
*/
raise_error("Cannot create unnamed attribute");
return false;
}
} else {
if (member.toString().empty()) {
raise_warning("Cannot write or create unnamed %s",
attribs ? "attribute" : "element");
return false;
}
}
bool retval = true;
xmlAttrPtr attr = nullptr;
xmlNodePtr mynode = nullptr;
bool test = false;
if (sxe->iter.type == SXE_ITER_ATTRLIST) {
attribs = true;
elements = false;
node = php_sxe_get_first_node(sxe, node);
attr = (xmlAttrPtr)node;
test = sxe->iter.name != nullptr;
} else if (sxe->iter.type != SXE_ITER_CHILD) {
mynode = node;
node = php_sxe_get_first_node(sxe, node);
attr = node ? node->properties : nullptr;
test = false;
if (member.isNull() && node && node->parent &&
node->parent->type == XML_DOCUMENT_NODE) {
/* This happens when the user did: $sxe[] = $value
* and could also be E_PARSE, but we use this only during parsing
* and this is during runtime.
*/
raise_error("Cannot create unnamed attribute");
return false;
}
if (attribs && !node && sxe->iter.type == SXE_ITER_ELEMENT) {
node = xmlNewChild(mynode, mynode->ns, sxe->iter.name, nullptr);
attr = node->properties;
}
}
mynode = node;
if (!(value.isString() || value.isInteger() || value.isBoolean() ||
value.isDouble() || value.isNull() || value.isObject())) {
raise_warning("It is not yet possible to assign complex types to %s",
attribs ? "attributes" : "properties");
return false;
}
xmlNodePtr newnode = nullptr;
if (node) {
int64_t nodendx = 0;
int64_t counter = 0;
bool is_attr = false;
if (attribs) {
if (member.isInteger()) {
while (attr && nodendx <= member.toInt64()) {
if ((!test || !xmlStrcmp(attr->name, sxe->iter.name)) &&
match_ns(sxe, (xmlNodePtr) attr, sxe->iter.nsprefix,
sxe->iter.isprefix)) {
if (nodendx == member.toInt64()) {
is_attr = true;
++counter;
break;
}
nodendx++;
}
attr = attr->next;
}
} else {
while (attr) {
if ((!test || !xmlStrcmp(attr->name, sxe->iter.name)) &&
!xmlStrcmp(attr->name, (xmlChar*)member.toString().data()) &&
match_ns(sxe, (xmlNodePtr) attr, sxe->iter.nsprefix,
sxe->iter.isprefix)) {
is_attr = true;
++counter;
break;
}
attr = attr->next;
}
}
}
long cnt = 0;
if (elements) {
if (member.isNull() || member.isInteger()) {
if (node->type == XML_ATTRIBUTE_NODE) {
raise_error("Cannot create duplicate attribute");
return false;
}
if (sxe->iter.type == SXE_ITER_NONE) {
newnode = node;
++counter;
if (!member.isNull() && member.toInt64() > 0) {
raise_warning("Cannot add element %s number %" PRId64 " when "
"only 0 such elements exist", mynode->name,
member.toInt64());
retval = false;
}
} else if (!member.isNull()) {
newnode =
sxe_get_element_by_offset(sxe, member.toInt64(), node, &cnt);
if (newnode) {
++counter;
}
}
} else {
node = node->children;
while (node) {
SKIP_TEXT(node)
if (!xmlStrcmp(node->name, (xmlChar*)member.toString().data())) {
newnode = node;
++counter;
}
next_iter:
node = node->next;
}
}
}
if (counter == 1) {
if (is_attr) {
newnode = (xmlNodePtr) attr;
}
if (!value.isNull()) {
xmlNodePtr tempnode;
while ((tempnode = (xmlNodePtr) newnode->children)) {
libxml_register_node(tempnode)->unlink();
}
change_node_zval(newnode, value);
}
} else if (counter > 1) {
raise_warning("Cannot assign to an array of nodes "
"(duplicate subnodes or attr detected)");
retval = false;
} else if (elements) {
if (!node) {
if (member.isNull() || member.isInteger()) {
newnode =
xmlNewTextChild(
mynode->parent, mynode->ns, mynode->name,
!value.isNull() ? (xmlChar*)value.toString().data() : nullptr);
} else {
newnode =
xmlNewTextChild(
mynode, mynode->ns, (xmlChar*)member.toString().data(),
!value.isNull() ? (xmlChar*)value.toString().data() : nullptr);
}
} else if (member.isNull() || member.isInteger()) {
if (!member.isNull() && cnt < member.toInt64()) {
raise_warning("Cannot add element %s number %" PRId64 " when "
"only %ld such elements exist", mynode->name,
member.toInt64(), cnt);
retval = false;
}
newnode = xmlNewTextChild(mynode->parent, mynode->ns, mynode->name,
!value.isNull() ?
(xmlChar*)value.toString().data() : nullptr);
}
} else if (attribs) {
if (member.isInteger()) {
raise_warning("Cannot change attribute number %" PRId64 " when "
"only %" PRId64 " attributes exist", member.toInt64(),
nodendx);
retval = false;
} else {
newnode = (xmlNodePtr)xmlNewProp(node,
(xmlChar*)member.toString().data(),
!value.isNull() ?
(xmlChar*)value.toString().data() :
nullptr);
}
}
}
if (pnewnode) {
*pnewnode = newnode;
}
return retval;
}
///////////////////////////////////////////////////////////////////////////////
// SimpleXML
static const Class* class_from_name(const String& class_name,
const char* callee) {
const Class* cls;
if (!class_name.empty()) {
cls = Class::load(class_name.get());
if (!cls) {
raise_invalid_argument_warning("class not found: %s", class_name.data());
return nullptr;
}
if (!cls->classof(SimpleXMLElement_classof())) {
raise_invalid_argument_warning(
"%s() expects parameter 2 to be a class name "
"derived from SimpleXMLElement, '%s' given",
callee,
class_name.data());
return nullptr;
}
} else {
cls = SimpleXMLElement_classof();
}
return cls;
}
const StaticString s_DOMNode("DOMNode");
static Variant HHVM_FUNCTION(simplexml_import_dom,
const Object& node,
const String& class_name) {
if (!node->instanceof(s_DOMNode)) {
raise_warning("Invalid Nodetype to import");
return init_null();
}
auto domnode = Native::data<DOMNode>(node);
xmlNodePtr nodep = domnode->nodep();
if (nodep) {
if (nodep->doc == nullptr) {
raise_warning("Imported Node must have associated Document");
return init_null();
}
if (nodep->type == XML_DOCUMENT_NODE ||
nodep->type == XML_HTML_DOCUMENT_NODE) {
nodep = xmlDocGetRootElement((xmlDocPtr) nodep);
}
}
if (nodep && nodep->type == XML_ELEMENT_NODE) {
auto cls = class_from_name(class_name, "simplexml_import_dom");
if (!cls) {
return init_null();
}
Object obj = create_object(cls->nameStr(), Array(), false);
auto sxe = Native::data<SimpleXMLElement>(obj.get());
sxe->node = libxml_register_node(nodep);
return obj;
} else {
raise_warning("Invalid Nodetype to import");
return init_null();
}
return false;
}
static Variant HHVM_FUNCTION(simplexml_load_string,
const String& data,
const String& class_name /* = "SimpleXMLElement" */,
int64_t options /* = 0 */,
const String& ns /* = "" */,
bool is_prefix /* = false */) {
SYNC_VM_REGS_SCOPED();
auto cls = class_from_name(class_name, "simplexml_load_string");
if (!cls) {
return init_null();
}
xmlDocPtr doc = xmlReadMemory(data.data(), data.size(), nullptr,
nullptr, options);
if (!doc) {
return false;
}
Object obj = create_object(cls->nameStr(), Array(), false);
auto sxe = Native::data<SimpleXMLElement>(obj.get());
sxe->node = libxml_register_node(xmlDocGetRootElement(doc));
sxe->iter.nsprefix = ns.size() ? xmlStrdup((xmlChar*)ns.data()) : nullptr;
sxe->iter.isprefix = is_prefix;
return obj;
}
static Variant
HHVM_FUNCTION(simplexml_load_file, const String& filename,
const String& class_name /* = "SimpleXMLElement" */,
int64_t /*options*/ /* = 0 */, const String& ns /* = "" */,
bool is_prefix /* = false */) {
SYNC_VM_REGS_SCOPED();
auto cls = class_from_name(class_name, "simplexml_load_file");
if (!cls) {
return init_null();
}
auto stream = File::Open(filename, "rb");
if (!stream || stream->isInvalid()) return false;
xmlDocPtr doc = nullptr;
// The XML context is also deleted in this function, so the ownership
// of the File is kept locally in 'stream'. The libxml_streams_IO_nop_close
// callback does nothing.
xmlParserCtxtPtr ctxt = xmlCreateIOParserCtxt(nullptr, nullptr,
libxml_streams_IO_read,
libxml_streams_IO_nop_close,
&stream,
XML_CHAR_ENCODING_NONE);
if (ctxt == nullptr) return false;
SCOPE_EXIT { xmlFreeParserCtxt(ctxt); };
if (ctxt->directory == nullptr) {
ctxt->directory = xmlParserGetDirectory(filename.c_str());
}
xmlParseDocument(ctxt);
if (ctxt->wellFormed) {
doc = ctxt->myDoc;
} else {
xmlFreeDoc(ctxt->myDoc);
ctxt->myDoc = nullptr;
return false;
}
Object obj = create_object(cls->nameStr(), Array(), false);
auto sxe = Native::data<SimpleXMLElement>(obj.get());
sxe->node = libxml_register_node(xmlDocGetRootElement(doc));
sxe->iter.nsprefix = ns.size() ? xmlStrdup((xmlChar*)ns.data()) : nullptr;
sxe->iter.isprefix = is_prefix;
return obj;
}
///////////////////////////////////////////////////////////////////////////////
// SimpleXMLElement
static void HHVM_METHOD(SimpleXMLElement, __construct,
const String& data,
int64_t options /* = 0 */,
bool data_is_url /* = false */,
const String& ns /* = "" */,
bool is_prefix /* = false */) {
SYNC_VM_REGS_SCOPED();
xmlDocPtr docp = data_is_url ?
xmlReadFile(data.data(), nullptr, options) :
xmlReadMemory(data.data(), data.size(), nullptr, nullptr, options);
if (!docp) {
SystemLib::throwExceptionObject("String could not be parsed as XML");
}
auto sxe = Native::data<SimpleXMLElement>(this_);
sxe->iter.nsprefix = !ns.empty() ? xmlStrdup((xmlChar*)ns.data()) : nullptr;
sxe->iter.isprefix = is_prefix;
sxe->node = libxml_register_node(xmlDocGetRootElement(docp));
}
static Variant HHVM_METHOD(SimpleXMLElement, xpath, const String& path) {
auto data = Native::data<SimpleXMLElement>(this_);
if (data->iter.type == SXE_ITER_ATTRLIST) {
return init_null(); // attributes don't have attributes
}
if (!data->xpath) {
data->xpath = xmlXPathNewContext(data->docp());
}
if (!data->nodep()) {
data->node = libxml_register_node(xmlDocGetRootElement(data->docp()));
}
auto nodeptr = php_sxe_get_first_node(data, data->nodep());
data->xpath->node = nodeptr;
xmlNsPtr* ns = xmlGetNsList(data->docp(), nodeptr);
int64_t nsnbr = 0;
if (ns != nullptr) {
while (ns[nsnbr] != nullptr) {
nsnbr++;
}
}
auto& xpath = data->xpath;
xpath->namespaces = ns;
xpath->nsNr = nsnbr;
xmlXPathObjectPtr retval = xmlXPathEval((xmlChar*)path.data(), xpath);
if (ns != nullptr) {
xmlFree(ns);
xpath->namespaces = nullptr;
xpath->nsNr = 0;
}
if (!retval) {
return false;
}
xmlNodeSetPtr result = retval->nodesetval;
Array ret = Array::CreateVec();
if (result != nullptr) {
for (int64_t i = 0; i < result->nodeNr; ++i) {
nodeptr = result->nodeTab[i];
if (nodeptr->type == XML_TEXT_NODE ||
nodeptr->type == XML_ELEMENT_NODE ||
nodeptr->type == XML_ATTRIBUTE_NODE) {
/**
* Detect the case where the last selector is text(), simplexml
* always accesses the text() child by default, therefore we assign
* to the parent node.
*/
Object obj;
if (nodeptr->type == XML_TEXT_NODE) {
obj = _node_as_zval(data, nodeptr->parent, SXE_ITER_NONE, nullptr,
nullptr, false);
} else if (nodeptr->type == XML_ATTRIBUTE_NODE) {
obj = _node_as_zval(data, nodeptr->parent, SXE_ITER_ATTRLIST,
(char*)nodeptr->name, nodeptr->ns ?
(xmlChar*)nodeptr->ns->href : nullptr, false);
} else {
obj = _node_as_zval(data, nodeptr, SXE_ITER_NONE, nullptr, nullptr,
false);
}
if (!obj.isNull()) {
ret.append(obj);
}
}
}
}
xmlXPathFreeObject(retval);
return ret;
}
static bool HHVM_METHOD(SimpleXMLElement, registerXPathNamespace,
const String& prefix, const String& ns) {
auto data = Native::data<SimpleXMLElement>(this_);
if (!data->xpath) {
data->xpath = xmlXPathNewContext(data->docp());
}
if (xmlXPathRegisterNs(data->xpath,
(xmlChar*)prefix.data(),
(xmlChar*)ns.data()) != 0) {
return false;
}
return true;
}
static Variant HHVM_METHOD(SimpleXMLElement, asXML,
const String& filename /* = "" */) {
auto data = Native::data<SimpleXMLElement>(this_);
xmlNodePtr node = data->nodep();
xmlOutputBufferPtr outbuf = nullptr;
if (filename.size()) {
node = php_sxe_get_first_node(data, node);
if (node) {
xmlDocPtr doc = data->docp();
if (node->parent && (XML_DOCUMENT_NODE == node->parent->type)) {
int bytes;
bytes = xmlSaveFile(filename.data(), doc);
if (bytes == -1) {
return false;
} else {
return true;
}
} else {
outbuf = xmlOutputBufferCreateFilename(filename.data(), nullptr, 0);
if (outbuf == nullptr) {
return false;
}
xmlNodeDumpOutput(outbuf, doc, node, 0, 0, nullptr);
xmlOutputBufferClose(outbuf);
return true;
}
} else {
return false;
}
}
node = php_sxe_get_first_node(data, node);
if (node) {
xmlDocPtr doc = data->docp();
if (node->parent && (XML_DOCUMENT_NODE == node->parent->type)) {
xmlChar* strval;
int strval_len;
xmlDocDumpMemoryEnc(doc, &strval, &strval_len,
(const char*)doc->encoding);
if (!strval) {
return false;
}
String ret = String((char*)strval);
xmlFree(strval);
return ret;
} else {
/* Should we be passing encoding information instead of nullptr? */
outbuf = xmlAllocOutputBuffer(nullptr);
if (outbuf == nullptr) {
return false;
}
xmlNodeDumpOutput(outbuf, doc, node, 0, 0,
(const char*)doc->encoding);
xmlOutputBufferFlush(outbuf);
char* str = nullptr;
#ifdef LIBXML2_NEW_BUFFER
str = (char*)xmlOutputBufferGetContent(outbuf);
#else
str = (char*)outbuf->buffer->content;
#endif
if (!str) {
return false;
}
String ret = String(str);
xmlOutputBufferClose(outbuf);
return ret;
}
} else {
return false;
}
return false;
}
static Array HHVM_METHOD(SimpleXMLElement, getNamespaces,
bool recursive /* = false */) {
auto data = Native::data<SimpleXMLElement>(this_);
Array ret = Array::CreateDict();
xmlNodePtr node = data->nodep();
node = php_sxe_get_first_node(data, node);
if (node) {
if (node->type == XML_ELEMENT_NODE) {
sxe_add_namespaces(data, node, recursive, ret);
} else if (node->type == XML_ATTRIBUTE_NODE && node->ns) {
sxe_add_namespace_name(ret, node->ns);
}
}
return ret;
}
static Array HHVM_METHOD(SimpleXMLElement, getDocNamespaces,
bool recursive /* = false */,
bool from_root /* = true */) {
auto data = Native::data<SimpleXMLElement>(this_);
xmlNodePtr node =
from_root ? xmlDocGetRootElement(data->docp())
: data->nodep();
Array ret = Array::CreateDict();
sxe_add_registered_namespaces(data, node, recursive, ret);
return ret;
}
static Variant HHVM_METHOD(SimpleXMLElement, children,
const String& ns = empty_string(),
bool is_prefix = false) {
auto data = Native::data<SimpleXMLElement>(this_);
if (data->iter.type == SXE_ITER_ATTRLIST) {
return init_null(); /* attributes don't have attributes */
}
xmlNodePtr node = data->nodep();
node = php_sxe_get_first_node(data, node);
return _node_as_zval(data, node, SXE_ITER_CHILD, nullptr,
(xmlChar*)ns.data(), is_prefix);
}
static String HHVM_METHOD(SimpleXMLElement, getName) {
auto data = Native::data<SimpleXMLElement>(this_);
xmlNodePtr node = data->nodep();
node = php_sxe_get_first_node(data, node);
if (node) {
return String((char*)node->name);
}
return empty_string();
}
static Object HHVM_METHOD(SimpleXMLElement, attributes,
const String& ns /* = "" */,
bool is_prefix /* = false */) {
auto data = Native::data<SimpleXMLElement>(this_);
if (data->iter.type == SXE_ITER_ATTRLIST) {
return Object(); /* attributes don't have attributes */
}
xmlNodePtr node = data->nodep();
node = php_sxe_get_first_node(data, node);
return _node_as_zval(data, node, SXE_ITER_ATTRLIST, nullptr,
(xmlChar*)ns.data(), is_prefix);
}
static Variant HHVM_METHOD(SimpleXMLElement, addChild,
const String& qname,
const String& value /* = null_string */,
const Variant& ns /* = null */) {
if (qname.empty()) {
raise_warning("Element name is required");
return init_null();
}
auto data = Native::data<SimpleXMLElement>(this_);
xmlNodePtr node = data->nodep();
if (data->iter.type == SXE_ITER_ATTRLIST) {
raise_warning("Cannot add element to attributes");
return init_null();
}
node = php_sxe_get_first_node(data, node);
if (node == nullptr) {
raise_warning("Cannot add child. "
"Parent is not a permanent member of the XML tree");
return init_null();
}
xmlChar* prefix = nullptr;
xmlChar* localname = xmlSplitQName2((xmlChar*)qname.data(), &prefix);
if (localname == nullptr) {
localname = xmlStrdup((xmlChar*)qname.data());
}
xmlNodePtr newnode = xmlNewChild(node, nullptr, localname,
(xmlChar*)value.data());
xmlNsPtr nsptr = nullptr;
if (!ns.isNull()) {
const String& ns_ = ns.toString();
if (ns_.empty()) {
newnode->ns = nullptr;
nsptr = xmlNewNs(newnode, (xmlChar*)ns_.data(), prefix);
} else {
nsptr = xmlSearchNsByHref(node->doc, node, (xmlChar*)ns_.data());
if (nsptr == nullptr) {
nsptr = xmlNewNs(newnode, (xmlChar*)ns_.data(), prefix);
}
newnode->ns = nsptr;
}
}
Object ret = _node_as_zval(data, newnode, SXE_ITER_NONE, (char*)localname,
prefix, false);
xmlFree(localname);
if (prefix != nullptr) {
xmlFree(prefix);
}
return ret;
}
static void HHVM_METHOD(SimpleXMLElement, addAttribute,
const String& qname,
const String& value /* = null_string */,
const String& ns /* = null_string */) {
if (qname.size() == 0) {
raise_warning("Attribute name is required");
return;
}
auto data = Native::data<SimpleXMLElement>(this_);
xmlNodePtr node = data->nodep();
node = php_sxe_get_first_node(data, node);
if (node && node->type != XML_ELEMENT_NODE) {
node = node->parent;
}
if (node == nullptr) {
raise_warning("Unable to locate parent Element");
return;
}
xmlChar* prefix = nullptr;
xmlChar* localname = xmlSplitQName2((xmlChar*)qname.data(), &prefix);
if (localname == nullptr) {
if (ns.size() > 0) {
if (prefix != nullptr) {
xmlFree(prefix);
}
raise_warning("Attribute requires prefix for namespace");
return;
}
localname = xmlStrdup((xmlChar*)qname.data());
}
xmlAttrPtr attrp = xmlHasNsProp(node, localname, (xmlChar*)ns.data());
if (attrp != nullptr && attrp->type != XML_ATTRIBUTE_DECL) {
xmlFree(localname);
if (prefix != nullptr) {
xmlFree(prefix);
}
raise_warning("Attribute already exists");
return;
}
xmlNsPtr nsptr = nullptr;
if (ns.size()) {
nsptr = xmlSearchNsByHref(node->doc, node, (xmlChar*)ns.data());
if (nsptr == nullptr) {
nsptr = xmlNewNs(node, (xmlChar*)ns.data(), prefix);
}
}
attrp = xmlNewNsProp(node, nsptr, localname, (xmlChar*)value.data());
xmlFree(localname);
if (prefix != nullptr) {
xmlFree(prefix);
}
}
static String HHVM_METHOD(SimpleXMLElement, __toString) {
return SimpleXMLElement_objectCast(this_, KindOfString).toString();
}
struct SimpleXMLElementPropHandler: Native::BasePropHandler {
static Variant getProp(const Object& this_, const String& name) {
auto data = Native::data<SimpleXMLElement>(this_.get());
return sxe_prop_dim_read(data, name, true, false);
}
static Variant setProp(const Object& this_,
const String& name,
const Variant& value) {
auto data = Native::data<SimpleXMLElement>(this_.get());
return sxe_prop_dim_write(data, name, value, true, false, nullptr);
return true;
}
static Variant issetProp(const Object& this_, const String& name) {
auto data = Native::data<SimpleXMLElement>(this_.get());
return sxe_prop_dim_exists(data, name, false, true, false);
}
static Variant unsetProp(const Object& this_, const String& name) {
auto data = Native::data<SimpleXMLElement>(this_.get());
sxe_prop_dim_delete(data, name, true, false);
return true;
}
static bool isPropSupported(const String&, const String&) {
// TODO: we really ought to pull out checking of whether a prop is supported
// to here, but it's currently very entagled in the normal functionality
// so defer that tech debt to later :p
return true;
}
};
static int64_t HHVM_METHOD(SimpleXMLElement, count) {
auto data = Native::data<SimpleXMLElement>(this_);
return php_sxe_count_elements_helper(data);
}
///////////////////////////////////////////////////////////////////////////////
// ArrayAccess
static bool HHVM_METHOD(SimpleXMLElement, offsetExists,
const Variant& index) {
auto data = Native::data<SimpleXMLElement>(this_);
return sxe_prop_dim_exists(data, index, false, false, true);
}
static Variant HHVM_METHOD(SimpleXMLElement, offsetGet,
const Variant& index) {
auto data = Native::data<SimpleXMLElement>(this_);
return sxe_prop_dim_read(data, index, false, true);
}
static void HHVM_METHOD(SimpleXMLElement, offsetSet,
const Variant& index, const Variant& newvalue) {
auto data = Native::data<SimpleXMLElement>(this_);
sxe_prop_dim_write(data, index, newvalue, false, true, nullptr);
}
static void HHVM_METHOD(SimpleXMLElement, offsetUnset,
const Variant& index) {
auto data = Native::data<SimpleXMLElement>(this_);
sxe_prop_dim_delete(data, index, false, true);
}
///////////////////////////////////////////////////////////////////////////////
// Iterator
static void HHVM_METHOD(SimpleXMLElementIterator, __construct,
const Variant& sxe) {
if (sxe.isObject()) {
Native::data<SimpleXMLElementIterator>(this_)->setSxe(sxe.toObject());
}
}
static Variant HHVM_METHOD(SimpleXMLElementIterator, current) {
return Native::data<SimpleXMLElementIterator>(this_)->sxe()->iter.data;
}
static Variant HHVM_METHOD(SimpleXMLElementIterator, key) {
auto sxe = Native::data<SimpleXMLElementIterator>(this_)->sxe();
Object curobj = sxe->iter.data;
xmlNodePtr curnode = curobj.isNull()
? nullptr
: Native::data<SimpleXMLElement>(curobj.get())->nodep();
if (curnode) {
return String((char*)curnode->name);
} else {
return init_null();
}
}
static Variant HHVM_METHOD(SimpleXMLElementIterator, next) {
auto sxe = Native::data<SimpleXMLElementIterator>(this_)->sxe();
php_sxe_move_forward_iterator(sxe);
return init_null();
}
static Variant HHVM_METHOD(SimpleXMLElementIterator, rewind) {
auto sxe = Native::data<SimpleXMLElementIterator>(this_)->sxe();
php_sxe_reset_iterator(sxe, true);
return init_null();
}
static Variant HHVM_METHOD(SimpleXMLElementIterator, valid) {
auto sxe = Native::data<SimpleXMLElementIterator>(this_)->sxe();
return !sxe->iter.data.isNull();
}
///////////////////////////////////////////////////////////////////////////////
// SimpleXMLIterator
static Variant HHVM_METHOD(SimpleXMLIterator, current) {
auto data = Native::data<SimpleXMLIterator>(this_);
return data->iter.data;
}
static Variant HHVM_METHOD(SimpleXMLIterator, key) {
auto data = Native::data<SimpleXMLIterator>(this_);
Object curobj = data->iter.data;
if (curobj.isNull()) {
return init_null();
}
assertx(curobj->instanceof(SimpleXMLElement_classof()));
auto curnode = Native::data<SimpleXMLElement>(curobj.get())->nodep();
return String((char*)curnode->name);
}
static Variant HHVM_METHOD(SimpleXMLIterator, next) {
auto data = Native::data<SimpleXMLIterator>(this_);
php_sxe_move_forward_iterator(data);
return init_null();
}
static Variant HHVM_METHOD(SimpleXMLIterator, rewind) {
auto data = Native::data<SimpleXMLIterator>(this_);
php_sxe_reset_iterator(data, true);
return init_null();
}
static Variant HHVM_METHOD(SimpleXMLIterator, valid) {
auto data = Native::data<SimpleXMLIterator>(this_);
return !data->iter.data.isNull();
}
static Variant HHVM_METHOD(SimpleXMLIterator, getChildren) {
auto data = Native::data<SimpleXMLIterator>(this_);
auto current = data->iter.data;
if (current.isNull()) {
return init_null();
}
assertx(current->instanceof(SimpleXMLElement_classof()));
return HHVM_MN(SimpleXMLElement, children)(current.get());
}
static bool HHVM_METHOD(SimpleXMLIterator, hasChildren) {
auto children = HHVM_MN(SimpleXMLIterator, getChildren)(this_);
if (!children.isObject()) {
return false;
}
auto od = children.toObject().get();
assertx(od->instanceof(SimpleXMLElement_classof()));
return HHVM_MN(SimpleXMLElement, count)(od) > 0;
}
///////////////////////////////////////////////////////////////////////////////
static struct SimpleXMLExtension : Extension {
SimpleXMLExtension(): Extension("simplexml", "1.0") {}
void moduleInit() override {
HHVM_FE(simplexml_import_dom);
HHVM_FE(simplexml_load_string);
HHVM_FE(simplexml_load_file);
/* SimpleXMLElement */
HHVM_ME(SimpleXMLElement, __construct);
HHVM_ME(SimpleXMLElement, xpath);
HHVM_ME(SimpleXMLElement, registerXPathNamespace);
HHVM_ME(SimpleXMLElement, asXML);
HHVM_ME(SimpleXMLElement, getNamespaces);
HHVM_ME(SimpleXMLElement, getDocNamespaces);
HHVM_ME(SimpleXMLElement, children);
HHVM_ME(SimpleXMLElement, getName);
HHVM_ME(SimpleXMLElement, attributes);
HHVM_ME(SimpleXMLElement, addChild);
HHVM_ME(SimpleXMLElement, addAttribute);
HHVM_ME(SimpleXMLElement, __toString);
HHVM_ME(SimpleXMLElement, count);
HHVM_ME(SimpleXMLElement, offsetExists);
HHVM_ME(SimpleXMLElement, offsetGet);
HHVM_ME(SimpleXMLElement, offsetSet);
HHVM_ME(SimpleXMLElement, offsetUnset);
Native::registerNativeDataInfo<SimpleXMLElement>(
s_SimpleXMLElement.get(), 0, Class::CallToImpl
);
Native::registerNativePropHandler<SimpleXMLElementPropHandler>(s_SimpleXMLElement);
/* SimpleXMLElementIterator */
HHVM_ME(SimpleXMLElementIterator, __construct);
HHVM_ME(SimpleXMLElementIterator, current);
HHVM_ME(SimpleXMLElementIterator, key);
HHVM_ME(SimpleXMLElementIterator, next);
HHVM_ME(SimpleXMLElementIterator, rewind);
HHVM_ME(SimpleXMLElementIterator, valid);
Native::registerNativeDataInfo<SimpleXMLElementIterator>(
s_SimpleXMLElementIterator.get(),
Native::NDIFlags::NO_SWEEP
);
/* SimpleXMLIterator */
HHVM_ME(SimpleXMLIterator, current);
HHVM_ME(SimpleXMLIterator, key);
HHVM_ME(SimpleXMLIterator, next);
HHVM_ME(SimpleXMLIterator, rewind);
HHVM_ME(SimpleXMLIterator, valid);
HHVM_ME(SimpleXMLIterator, getChildren);
HHVM_ME(SimpleXMLIterator, hasChildren);
Native::registerNativeDataInfo<SimpleXMLIterator>(
s_SimpleXMLIterator.get(), 0, Class::CallToImpl
);
loadSystemlib();
}
} s_simplexml_extension;
///////////////////////////////////////////////////////////////////////////////
}