84b9d9a3a2
In HHVM (and HPHPc before it) we've been piggybacking resources on the KindOfObject machinery. At the language level, resource is considered to be a different type than object, and there are a number of differences in behavior between objects and resources (ex. resources don't allow for dynamic properties, resources don't work with the clone operator, the "(object)" cast behaves differently for resources vs. objects, etc). Piggybacking resources on the KindOfObject machinery has some downsides. Code that deals with KindOfObject values often needs to check if the value is a resource and go down a different code path. This makes things harder to maintain and harder to keep parity with Zend. Also, these extra branches hurt performance a little, and they make it harder for the JIT to do a good job in some cases when its generating machine code that operates on objects. This diff prepares the code base for a new KindOfResource type by adding a new "Resource" smart pointer type (currently a typedef for the Object smart pointer type) and it updates the C++ code and the idl files appropriately. This diff is essentially a cosmetic change and should not impact run time behavior. In the next diff (part 2) we'll actually add a new KindOfResource type, detach ResourceData from the ObjectData inheritence hierarchy, and provide a real implementation for the Resource smart pointer type (instead of just aliasing the Object smart pointer type).
1300 linhas
37 KiB
C++
1300 linhas
37 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010-2013 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/ext/ext_simplexml.h"
|
|
#include "hphp/runtime/ext/ext_file.h"
|
|
#include "hphp/runtime/ext/ext_class.h"
|
|
#include "hphp/runtime/ext/ext_domdocument.h"
|
|
#include "hphp/runtime/base/class_info.h"
|
|
#include "hphp/runtime/base/util/request_local.h"
|
|
#include "hphp/system/systemlib.h"
|
|
|
|
#ifndef LIBXML2_NEW_BUFFER
|
|
# define xmlOutputBufferGetSize(buf) ((buf)->buffer->use)
|
|
# define xmlOutputBufferGetContent(buf) ((buf)->buffer->content)
|
|
#endif
|
|
|
|
namespace HPHP {
|
|
IMPLEMENT_DEFAULT_EXTENSION(SimpleXML);
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// This is to make sure each node holds one reference of m_doc, so not to let
|
|
// it go out of scope.
|
|
class XmlDocWrapper : public SweepableResourceData {
|
|
public:
|
|
DECLARE_OBJECT_ALLOCATION_NO_SWEEP(XmlDocWrapper)
|
|
|
|
static StaticString s_class_name;
|
|
// overriding ResourceData
|
|
virtual CStrRef o_getClassNameHook() const { return s_class_name; }
|
|
|
|
XmlDocWrapper(xmlDocPtr doc, CStrRef cls, Object domNode = nullptr)
|
|
: m_doc(doc), m_cls(cls), m_domNode(domNode) {
|
|
if (!domNode.isNull()) {
|
|
DEBUG_ONLY c_DOMNode *domnode = domNode.getTyped<c_DOMNode>();
|
|
assert(!domnode || domnode->m_node == (xmlNodePtr) doc);
|
|
}
|
|
}
|
|
|
|
CStrRef getClass() { return m_cls; }
|
|
|
|
void sweep() {
|
|
// if m_domNode isn't null, then he owns the m_doc. Otherwise, I own it
|
|
if (m_doc && m_domNode.isNull()) {
|
|
xmlFreeDoc(m_doc);
|
|
}
|
|
}
|
|
~XmlDocWrapper() { XmlDocWrapper::sweep(); }
|
|
private:
|
|
xmlDocPtr m_doc;
|
|
String m_cls;
|
|
// Hold onto the original owner of the doc so it doesn't get free()d.
|
|
Object m_domNode;
|
|
};
|
|
IMPLEMENT_OBJECT_ALLOCATION_NO_DEFAULT_SWEEP(XmlDocWrapper)
|
|
|
|
StaticString XmlDocWrapper::s_class_name("xmlDoc");
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// helpers
|
|
|
|
static inline bool match_ns(xmlNodePtr node, CStrRef ns, bool is_prefix) {
|
|
if (ns.empty()) {
|
|
return true;
|
|
}
|
|
if (node->ns == NULL || node->ns->prefix == NULL) {
|
|
return false;
|
|
}
|
|
if (node->ns && !xmlStrcmp(is_prefix ? node->ns->prefix : node->ns->href,
|
|
(const xmlChar *)ns.data())) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static String node_list_to_string(xmlDocPtr doc, xmlNodePtr list) {
|
|
xmlChar *tmp = xmlNodeListGetString(doc, list, 1);
|
|
String res((char*) tmp, CopyString);
|
|
xmlFree(tmp);
|
|
return res;
|
|
}
|
|
|
|
static Array collect_attributes(xmlNodePtr node, CStrRef ns, bool is_prefix) {
|
|
assert(node);
|
|
Array attributes = Array::Create();
|
|
if (node->type != XML_ENTITY_DECL) {
|
|
for (xmlAttrPtr attr = node->properties; attr; attr = attr->next) {
|
|
if (match_ns((xmlNodePtr)attr, ns, is_prefix)) {
|
|
String n = String((char*)attr->name, xmlStrlen(attr->name), CopyString);
|
|
attributes.set(n, node_list_to_string(node->doc, attr->children));
|
|
}
|
|
}
|
|
}
|
|
return attributes;
|
|
}
|
|
|
|
static void add_property(Array &properties, xmlNodePtr node, Object value) {
|
|
const char *name = (char *)node->name;
|
|
if (name) {
|
|
int namelen = xmlStrlen(node->name);
|
|
String sname(name, namelen, CopyString);
|
|
|
|
if (properties.exists(sname)) {
|
|
Variant &existing = properties.lval(sname);
|
|
if (existing.is(KindOfArray)) {
|
|
existing.append(value);
|
|
} else {
|
|
Array newdata;
|
|
newdata.append(existing);
|
|
newdata.append(value);
|
|
properties.set(sname, newdata);
|
|
}
|
|
} else {
|
|
properties.set(sname, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
static Object create_text(CResRef doc, xmlNodePtr node,
|
|
CStrRef value, CStrRef ns,
|
|
bool is_prefix, bool free_text) {
|
|
Object obj = create_object(doc.getTyped<XmlDocWrapper>()->
|
|
getClass(), Array(), false);
|
|
c_SimpleXMLElement *elem = obj.getTyped<c_SimpleXMLElement>();
|
|
elem->m_doc = doc;
|
|
elem->m_node = node->parent; // assign to parent, not node
|
|
elem->m_children.set(0, value);
|
|
elem->m_is_text = true;
|
|
elem->m_free_text = free_text;
|
|
elem->m_attributes = collect_attributes(node->parent, ns, is_prefix);
|
|
return obj;
|
|
}
|
|
|
|
static Array create_children(CResRef doc, xmlNodePtr root,
|
|
CStrRef ns, bool is_prefix);
|
|
|
|
static Object create_element(CResRef doc, xmlNodePtr node,
|
|
CStrRef ns, bool is_prefix) {
|
|
Object obj = create_object(doc.getTyped<XmlDocWrapper>()->
|
|
getClass(), Array(), false);
|
|
c_SimpleXMLElement *elem = obj.getTyped<c_SimpleXMLElement>();
|
|
elem->m_doc = doc;
|
|
elem->m_node = node;
|
|
if (node) {
|
|
elem->m_children = create_children(doc, node, ns, is_prefix);
|
|
elem->m_attributes = collect_attributes(node, ns, is_prefix);
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
static Array create_children(CResRef doc, xmlNodePtr root,
|
|
CStrRef ns, bool is_prefix) {
|
|
Array properties = Array::Create();
|
|
for (xmlNodePtr node = root->children; node; node = node->next) {
|
|
if (node->children || node->prev || node->next) {
|
|
if (node->type == XML_TEXT_NODE) {
|
|
// bad node from parser, ignoring it...
|
|
continue;
|
|
}
|
|
} else {
|
|
if (node->type == XML_TEXT_NODE) {
|
|
if (node->content && *node->content) {
|
|
add_property
|
|
(properties, root,
|
|
create_text(doc, node, node_list_to_string(root->doc, node),
|
|
ns, is_prefix, true));
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (node->type != XML_ELEMENT_NODE || match_ns(node, ns, is_prefix)) {
|
|
xmlNodePtr child = node->children;
|
|
Object sub;
|
|
if (child && child->type == XML_TEXT_NODE && !xmlIsBlankNode(child)) {
|
|
sub = create_text(doc, child, node_list_to_string(root->doc, child),
|
|
ns, is_prefix, false);
|
|
} else {
|
|
sub = create_element(doc, node, ns, is_prefix);
|
|
}
|
|
add_property(properties, node, sub);
|
|
}
|
|
}
|
|
return properties;
|
|
}
|
|
|
|
static inline void add_namespace_name(Array &out, xmlNsPtr ns) {
|
|
String prefix = ns->prefix ? String((const char*)ns->prefix) :
|
|
String(empty_string);
|
|
if (!out.exists(prefix)) {
|
|
out.set(prefix, String((char*)ns->href, CopyString));
|
|
}
|
|
}
|
|
|
|
static void add_namespaces(Array &out, xmlNodePtr node, bool recursive) {
|
|
if (node->ns) {
|
|
add_namespace_name(out, node->ns);
|
|
}
|
|
|
|
for (xmlAttrPtr attr = node->properties; attr; attr = attr->next) {
|
|
if (attr->ns) {
|
|
add_namespace_name(out, attr->ns);
|
|
}
|
|
}
|
|
|
|
if (recursive) {
|
|
for (node = node->children; node; node = node->next) {
|
|
if (node->type == XML_ELEMENT_NODE) {
|
|
add_namespaces(out, node, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void add_registered_namespaces(Array &out, xmlNodePtr node,
|
|
bool recursive) {
|
|
if (node->type == XML_ELEMENT_NODE) {
|
|
for (xmlNsPtr ns = node->nsDef; ns; ns = ns->next) {
|
|
add_namespace_name(out, ns);
|
|
}
|
|
if (recursive) {
|
|
for (node = node->children; node; node = node->next) {
|
|
add_registered_namespaces(out, node, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// simplexml
|
|
|
|
Variant f_simplexml_import_dom(CObjRef node,
|
|
CStrRef class_name /* = "SimpleXMLElement" */) {
|
|
|
|
c_DOMNode *domnode = node.getTyped<c_DOMNode>();
|
|
xmlNodePtr nodep = domnode->m_node;
|
|
|
|
if (nodep) {
|
|
if (nodep->doc == nullptr) {
|
|
raise_warning("Imported Node must have associated Document");
|
|
return uninit_null();
|
|
}
|
|
if (nodep->type == XML_DOCUMENT_NODE ||
|
|
nodep->type == XML_HTML_DOCUMENT_NODE) {
|
|
nodep = xmlDocGetRootElement((xmlDocPtr) nodep);
|
|
}
|
|
}
|
|
|
|
if (nodep && nodep->type == XML_ELEMENT_NODE) {
|
|
Resource obj =
|
|
Resource(NEWOBJ(XmlDocWrapper)(nodep->doc, class_name, node));
|
|
return create_element(obj, nodep, String(), false);
|
|
} else {
|
|
raise_warning("Invalid Nodetype to import");
|
|
return uninit_null();
|
|
}
|
|
}
|
|
|
|
Variant f_simplexml_load_string(CStrRef data,
|
|
CStrRef class_name /* = "SimpleXMLElement" */,
|
|
int64_t options /* = 0 */,
|
|
CStrRef ns /* = "" */,
|
|
bool is_prefix /* = false */) {
|
|
Class* cls;
|
|
if (!class_name.empty()) {
|
|
cls = Unit::loadClass(class_name.get());
|
|
if (!cls) {
|
|
throw_invalid_argument("class not found: %s", class_name.data());
|
|
return uninit_null();
|
|
}
|
|
if (!cls->classof(c_SimpleXMLElement::s_cls)) {
|
|
throw_invalid_argument(
|
|
"simplexml_load_string() expects parameter 2 to be a class name "
|
|
"derived from SimpleXMLElement, '%s' given",
|
|
class_name.data());
|
|
return uninit_null();
|
|
}
|
|
} else {
|
|
cls = c_SimpleXMLElement::s_cls;
|
|
}
|
|
|
|
xmlDocPtr doc = xmlReadMemory(data.data(), data.size(), NULL, NULL, options);
|
|
xmlNodePtr root = xmlDocGetRootElement(doc);
|
|
if (!doc) {
|
|
return false;
|
|
}
|
|
|
|
return create_element(Resource(NEWOBJ(XmlDocWrapper)(doc, cls->nameRef())),
|
|
root, ns, is_prefix);
|
|
}
|
|
|
|
Variant f_simplexml_load_file(CStrRef filename,
|
|
CStrRef class_name /* = "SimpleXMLElement" */,
|
|
int64_t options /* = 0 */, CStrRef ns /* = "" */,
|
|
bool is_prefix /* = false */) {
|
|
String str = f_file_get_contents(filename);
|
|
return f_simplexml_load_string(str, class_name, options, ns, is_prefix);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// SimpleXMLElement
|
|
|
|
c_SimpleXMLElement::c_SimpleXMLElement(Class* cb) :
|
|
ExtObjectDataFlags<ObjectData::UseGet|
|
|
ObjectData::UseSet|
|
|
ObjectData::UseIsset|
|
|
ObjectData::UseUnset|
|
|
ObjectData::CallToImpl>(cb),
|
|
m_node(NULL), m_is_text(false), m_free_text(false),
|
|
m_is_attribute(false), m_is_children(false), m_is_property(false),
|
|
m_xpath(NULL) {
|
|
m_children = Array::Create();
|
|
}
|
|
|
|
c_SimpleXMLElement::~c_SimpleXMLElement() {
|
|
c_SimpleXMLElement::sweep();
|
|
}
|
|
|
|
void c_SimpleXMLElement::sweep() {
|
|
if (m_xpath) {
|
|
xmlXPathFreeContext(m_xpath);
|
|
}
|
|
}
|
|
|
|
void c_SimpleXMLElement::t___construct(CStrRef data, int64_t options /* = 0 */,
|
|
bool data_is_url /* = false */,
|
|
CStrRef ns /* = "" */,
|
|
bool is_prefix /* = false */) {
|
|
String xml = data;
|
|
if (data_is_url) {
|
|
Variant ret = f_file_get_contents(data);
|
|
if (same(ret, false)) {
|
|
raise_warning("Unable to retrieve XML content from %s", data.data());
|
|
return;
|
|
}
|
|
xml = ret.toString();
|
|
}
|
|
|
|
xmlDocPtr doc = xmlReadMemory(xml.data(), xml.size(), NULL, NULL, options);
|
|
if (doc) {
|
|
m_doc =
|
|
Resource(NEWOBJ(XmlDocWrapper)(doc, c_SimpleXMLElement::s_class_name));
|
|
m_node = xmlDocGetRootElement(doc);
|
|
if (m_node) {
|
|
m_children = create_children(m_doc, m_node, ns, is_prefix);
|
|
m_attributes = collect_attributes(m_node, ns, is_prefix);
|
|
}
|
|
} else {
|
|
throw Object(SystemLib::AllocExceptionObject(
|
|
"String could not be parsed as XML"));
|
|
}
|
|
}
|
|
|
|
Variant c_SimpleXMLElement::t_xpath(CStrRef path) {
|
|
if (m_is_attribute || !m_node) {
|
|
return uninit_null();
|
|
}
|
|
|
|
xmlDocPtr doc = m_node->doc;
|
|
|
|
int nsnbr = 0;
|
|
xmlNsPtr *ns = xmlGetNsList(doc, m_node);
|
|
if (ns != NULL) {
|
|
while (ns[nsnbr] != NULL) {
|
|
nsnbr++;
|
|
}
|
|
}
|
|
|
|
if (m_xpath == NULL) {
|
|
m_xpath = xmlXPathNewContext(doc);
|
|
}
|
|
m_xpath->node = m_node;
|
|
m_xpath->namespaces = ns;
|
|
m_xpath->nsNr = nsnbr;
|
|
|
|
xmlXPathObjectPtr retval = xmlXPathEval((xmlChar *)path.data(), m_xpath);
|
|
if (ns != NULL) {
|
|
xmlFree(ns);
|
|
m_xpath->namespaces = NULL;
|
|
m_xpath->nsNr = 0;
|
|
}
|
|
|
|
if (!retval) {
|
|
return false;
|
|
}
|
|
|
|
xmlNodeSetPtr result = retval->nodesetval;
|
|
if (!result) {
|
|
xmlXPathFreeObject(retval);
|
|
return false;
|
|
}
|
|
|
|
Array ret = Array::Create();
|
|
for (int i = 0; i < result->nodeNr; ++i) {
|
|
xmlNodePtr nodeptr = result->nodeTab[i];
|
|
Object sub;
|
|
/**
|
|
* Detect the case where the last selector is text(), simplexml
|
|
* always accesses the text() child by default, therefore we assign
|
|
* to the parent node.
|
|
*/
|
|
switch (nodeptr->type) {
|
|
case XML_TEXT_NODE:
|
|
sub = create_element(m_doc, nodeptr->parent, String(), false);
|
|
break;
|
|
case XML_ELEMENT_NODE:
|
|
sub = create_element(m_doc, nodeptr, String(), false);
|
|
break;
|
|
case XML_ATTRIBUTE_NODE:
|
|
sub = create_element(m_doc, nodeptr->parent, String(), false);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
ret.append(sub);
|
|
}
|
|
|
|
xmlXPathFreeObject(retval);
|
|
return ret;
|
|
}
|
|
|
|
bool c_SimpleXMLElement::t_registerxpathnamespace(CStrRef prefix, CStrRef ns) {
|
|
if (m_node) {
|
|
if (!m_xpath) {
|
|
m_xpath = xmlXPathNewContext(m_node->doc);
|
|
}
|
|
return xmlXPathRegisterNs(m_xpath, (xmlChar *)prefix.data(),
|
|
(xmlChar *)ns.data()) == 0;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Variant c_SimpleXMLElement::t_asxml(CStrRef filename /* = "" */) {
|
|
if (!m_node) return false;
|
|
|
|
if (!filename.empty()) {
|
|
std::string translated = File::TranslatePath(filename).data();
|
|
|
|
if (m_node->parent && m_node->parent->type == XML_DOCUMENT_NODE) {
|
|
int bytes = xmlSaveFile(translated.c_str(), (xmlDocPtr)m_node->doc);
|
|
return bytes != -1;
|
|
}
|
|
|
|
xmlOutputBufferPtr outbuf =
|
|
xmlOutputBufferCreateFilename(translated.c_str(), NULL, 0);
|
|
if (outbuf == NULL) {
|
|
return false;
|
|
}
|
|
xmlNodeDumpOutput(outbuf, m_node->doc, m_node, 0, 0,
|
|
(char*)m_node->doc->encoding);
|
|
xmlOutputBufferClose(outbuf);
|
|
return true;
|
|
}
|
|
|
|
xmlChar *strval;
|
|
int strval_len;
|
|
if (m_node->parent && m_node->parent->type == XML_DOCUMENT_NODE) {
|
|
xmlDocDumpMemory(m_node->doc, &strval, &strval_len);
|
|
String ret((char *)strval, strval_len, CopyString);
|
|
xmlFree(strval);
|
|
return ret;
|
|
}
|
|
|
|
xmlOutputBufferPtr outbuf = xmlAllocOutputBuffer(NULL);
|
|
if (outbuf == NULL) {
|
|
return false;
|
|
}
|
|
xmlNodeDumpOutput(outbuf, m_node->doc, m_node, 0, 0,
|
|
(char*)m_node->doc->encoding);
|
|
xmlOutputBufferFlush(outbuf);
|
|
String ret((char *)xmlOutputBufferGetContent(outbuf),
|
|
xmlOutputBufferGetSize(outbuf), CopyString);
|
|
xmlOutputBufferClose(outbuf);
|
|
return ret;
|
|
}
|
|
|
|
Array c_SimpleXMLElement::t_getnamespaces(bool recursive /* = false */) {
|
|
Array ret = Array::Create();
|
|
if (m_node) {
|
|
if (m_node->type == XML_ELEMENT_NODE) {
|
|
add_namespaces(ret, m_node, recursive);
|
|
} else if (m_node->type == XML_ATTRIBUTE_NODE && m_node->ns) {
|
|
add_namespace_name(ret, m_node->ns);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Array c_SimpleXMLElement::t_getdocnamespaces(bool recursive /* = false */) {
|
|
Array ret = Array::Create();
|
|
if (m_node) {
|
|
add_registered_namespaces(ret, xmlDocGetRootElement(m_node->doc),
|
|
recursive);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Object c_SimpleXMLElement::t_children(CStrRef ns /* = "" */,
|
|
bool is_prefix /* = false */) {
|
|
if (m_is_attribute) {
|
|
return Object();
|
|
}
|
|
|
|
Object obj = create_object(m_doc.getTyped<XmlDocWrapper>()->
|
|
getClass(), Array(), false);
|
|
c_SimpleXMLElement *elem = obj.getTyped<c_SimpleXMLElement>();
|
|
elem->m_doc = m_doc;
|
|
elem->m_node = m_node;
|
|
elem->m_is_text = m_is_text;
|
|
elem->m_free_text = m_free_text;
|
|
elem->m_is_children = true;
|
|
if (ns.empty()) {
|
|
elem->m_children.assignRef(m_children);
|
|
} else {
|
|
Array props = Array::Create();
|
|
for (ArrayIter iter(m_children.toArray()); iter; ++iter) {
|
|
if (iter.second().isObject()) {
|
|
c_SimpleXMLElement *elem = iter.second().toObject().
|
|
getTyped<c_SimpleXMLElement>();
|
|
if (elem->m_node && match_ns(elem->m_node, ns, is_prefix)) {
|
|
props.set(iter.first(), iter.second());
|
|
}
|
|
} else {
|
|
Array subnodes;
|
|
for (ArrayIter iter2(iter.second().toArray()); iter2; ++iter2) {
|
|
c_SimpleXMLElement *elem = iter2.second().toObject().
|
|
getTyped<c_SimpleXMLElement>();
|
|
if (elem->m_node && match_ns(elem->m_node, ns, is_prefix)) {
|
|
subnodes.append(iter2.second());
|
|
}
|
|
}
|
|
if (!subnodes.empty()) {
|
|
if (subnodes.size() == 1) {
|
|
props.set(iter.first(), subnodes[0]);
|
|
} else {
|
|
props.set(iter.first(), subnodes);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
elem->m_children = props;
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
String c_SimpleXMLElement::t_getname() {
|
|
if (m_is_children) {
|
|
Variant first;
|
|
ArrayIter iter(m_children.toArray());
|
|
if (iter) {
|
|
return iter.first();
|
|
}
|
|
} else if (m_node) {
|
|
int namelen = xmlStrlen(m_node->name);
|
|
return String((char*)m_node->name, namelen, CopyString);
|
|
}
|
|
return String();
|
|
}
|
|
|
|
static const StaticString s_attributes("@attributes");
|
|
|
|
Object c_SimpleXMLElement::t_attributes(CStrRef ns /* = "" */,
|
|
bool is_prefix /* = false */) {
|
|
if (m_is_attribute) {
|
|
return Object();
|
|
}
|
|
|
|
Object obj = create_object(m_doc.getTyped<XmlDocWrapper>()->
|
|
getClass(), Array(), false);
|
|
c_SimpleXMLElement *elem = obj.getTyped<c_SimpleXMLElement>();
|
|
elem->m_doc = m_doc;
|
|
elem->m_node = m_node;
|
|
elem->m_is_attribute = true;
|
|
if (!m_attributes.toArray().empty()) {
|
|
if (!ns.empty()) {
|
|
elem->m_attributes = collect_attributes(m_node, ns, is_prefix);
|
|
} else {
|
|
elem->m_attributes.assignRef(m_attributes);
|
|
}
|
|
elem->m_children.set(s_attributes, elem->m_attributes);
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
Variant c_SimpleXMLElement::t_addchild(CStrRef qname,
|
|
CStrRef value /* = null_string */,
|
|
CStrRef ns /* = null_string */) {
|
|
if (qname.empty()) {
|
|
raise_warning("Element name is required");
|
|
return uninit_null();
|
|
}
|
|
if (m_is_attribute) {
|
|
raise_warning("Cannot add element to attributes");
|
|
return uninit_null();
|
|
}
|
|
if (!m_node) {
|
|
raise_warning("Parent is not a permanent member of the XML tree");
|
|
return uninit_null();
|
|
}
|
|
|
|
xmlChar *prefix = NULL;
|
|
xmlChar *localname = xmlSplitQName2((xmlChar *)qname.data(), &prefix);
|
|
if (localname == NULL) {
|
|
localname = xmlStrdup((xmlChar *)qname.data());
|
|
}
|
|
|
|
xmlNsPtr nsptr = NULL;
|
|
xmlNodePtr newnode = xmlNewChild(m_node, NULL, localname,
|
|
(xmlChar *)value.data());
|
|
if (!ns.isNull()) {
|
|
if (ns.empty()) {
|
|
newnode->ns = NULL;
|
|
nsptr = xmlNewNs(newnode, (xmlChar *)ns.data(), prefix);
|
|
} else {
|
|
nsptr = xmlSearchNsByHref(m_node->doc, m_node, (xmlChar *)ns.data());
|
|
if (nsptr == NULL) {
|
|
nsptr = xmlNewNs(newnode, (xmlChar *)ns.data(), prefix);
|
|
}
|
|
newnode->ns = nsptr;
|
|
}
|
|
}
|
|
|
|
String newname((char*)localname, CopyString);
|
|
String newns((char*)prefix, CopyString);
|
|
xmlFree(localname);
|
|
if (prefix) {
|
|
xmlFree(prefix);
|
|
}
|
|
|
|
Object child = create_element(m_doc, newnode, newns, false);
|
|
if (m_children.toArray().exists(newname)) {
|
|
Variant &tmp = m_children.lvalAt(newname);
|
|
if (tmp.isArray()) {
|
|
tmp.append(child);
|
|
} else {
|
|
Array arr;
|
|
arr.append(tmp);
|
|
arr.append(child);
|
|
m_children.set(newname, arr);
|
|
}
|
|
} else {
|
|
m_children.set(newname, child);
|
|
}
|
|
return child;
|
|
}
|
|
|
|
void c_SimpleXMLElement::t_addattribute(CStrRef qname,
|
|
CStrRef value /* = null_string */,
|
|
CStrRef ns /* = null_string */) {
|
|
if (qname.empty()) {
|
|
raise_warning("Attribute name is required");
|
|
return;
|
|
}
|
|
|
|
if (m_node && m_node->type != XML_ELEMENT_NODE) {
|
|
m_node = m_node->parent;
|
|
}
|
|
if (!m_node) {
|
|
raise_warning("Unable to locate parent Element");
|
|
return;
|
|
}
|
|
|
|
xmlChar *prefix = NULL;
|
|
xmlChar *localname = xmlSplitQName2((xmlChar *)qname.data(), &prefix);
|
|
if (localname == NULL) {
|
|
localname = xmlStrdup((xmlChar *)qname.data());
|
|
}
|
|
|
|
xmlAttrPtr attrp = xmlHasNsProp(m_node, localname, (xmlChar *)ns.data());
|
|
if (attrp && attrp->type != XML_ATTRIBUTE_DECL) {
|
|
xmlFree(localname);
|
|
if (prefix != NULL) {
|
|
xmlFree(prefix);
|
|
}
|
|
raise_warning("Attribute already exists");
|
|
return;
|
|
}
|
|
|
|
xmlNsPtr nsptr = NULL;
|
|
if (!ns.isNull()) {
|
|
nsptr = xmlSearchNsByHref(m_node->doc, m_node, (xmlChar *)ns.data());
|
|
if (nsptr == NULL) {
|
|
nsptr = xmlNewNs(m_node, (xmlChar *)ns.data(), prefix);
|
|
}
|
|
}
|
|
|
|
attrp = xmlNewNsProp(m_node, nsptr, localname, (xmlChar *)value.data());
|
|
m_attributes.set(String((char*)localname, CopyString), value);
|
|
|
|
xmlFree(localname);
|
|
if (prefix != NULL) {
|
|
xmlFree(prefix);
|
|
}
|
|
}
|
|
|
|
String c_SimpleXMLElement::t___tostring() {
|
|
Variant prop;
|
|
ArrayIter iter(m_children.toArray());
|
|
if (iter) {
|
|
prop = iter.second();
|
|
if (prop.isString()) {
|
|
return prop.toString();
|
|
}
|
|
if (prop.isObject()) {
|
|
c_SimpleXMLElement *elem =
|
|
prop.toObject().getTyped<c_SimpleXMLElement>();
|
|
if (elem->m_is_text && elem->m_free_text) {
|
|
return prop.toString();
|
|
}
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
Variant c_SimpleXMLElement::t___get(Variant name) {
|
|
Variant ret = m_children[name];
|
|
if (ret.isArray()) {
|
|
ret = ret[0];
|
|
}
|
|
if (ret.isObject()) {
|
|
c_SimpleXMLElement *elem = ret.toObject().getTyped<c_SimpleXMLElement>();
|
|
Object obj = create_object(m_doc.getTyped<XmlDocWrapper>()->
|
|
getClass(), Array(), false);
|
|
c_SimpleXMLElement *e = obj.getTyped<c_SimpleXMLElement>();
|
|
e->m_doc = elem->m_doc;
|
|
e->m_node = elem->m_node;
|
|
e->m_children.assignRef(elem->m_children);
|
|
e->m_attributes.assignRef(elem->m_attributes);
|
|
e->m_is_text = elem->m_is_text;
|
|
e->m_is_property = true;
|
|
return obj;
|
|
}
|
|
if (ret.isNull()) {
|
|
return create_object(o_getClassName(), Array(), false);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Variant c_SimpleXMLElement::t___unset(Variant name) {
|
|
if (m_node == NULL) return uninit_null();
|
|
|
|
Variant node;
|
|
if (m_is_attribute) {
|
|
node = m_attributes[name];
|
|
} else {
|
|
node = m_children[name];
|
|
}
|
|
|
|
if (node.isObject()) {
|
|
c_SimpleXMLElement *elem =
|
|
node.toObject().getTyped<c_SimpleXMLElement>();
|
|
if (elem->m_node) {
|
|
xmlUnlinkNode(elem->m_node);
|
|
}
|
|
} else if (node.isArray()) {
|
|
for (ArrayIter iter(node.toArray()); iter; ++iter) {
|
|
c_SimpleXMLElement *elem = iter.second().toObject().
|
|
getTyped<c_SimpleXMLElement>();
|
|
if (elem->m_node) {
|
|
xmlUnlinkNode(elem->m_node);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_is_attribute) {
|
|
m_attributes.remove(name);
|
|
} else {
|
|
m_children.remove(name);
|
|
}
|
|
return uninit_null();
|
|
}
|
|
|
|
bool c_SimpleXMLElement::t___isset(Variant name) {
|
|
if (m_node) {
|
|
if (m_is_attribute) {
|
|
return m_attributes.toArray().exists(name);
|
|
} else {
|
|
return m_children.toArray().exists(name);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void change_node_zval(xmlNodePtr node, CStrRef value) {
|
|
if (value.empty()) {
|
|
xmlNodeSetContentLen(node, (xmlChar *)"", 0);
|
|
} else {
|
|
xmlChar *buffer =
|
|
xmlEncodeEntitiesReentrant(node->doc, (xmlChar *)value.data());
|
|
int buffer_len = xmlStrlen(buffer);
|
|
if (buffer) {
|
|
xmlNodeSetContentLen(node, buffer, buffer_len);
|
|
xmlFree(buffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
Variant c_SimpleXMLElement::t___set(Variant name, Variant value) {
|
|
if (m_node == NULL) return uninit_null();
|
|
|
|
String svalue = value.toString();
|
|
xmlChar *sv = svalue.empty() ? NULL : (xmlChar *)svalue.data();
|
|
String sname = name.toString();
|
|
|
|
Variant node;
|
|
if (m_is_attribute) {
|
|
node = m_attributes[name];
|
|
} else {
|
|
node = m_children[name];
|
|
}
|
|
|
|
xmlNodePtr newnode = NULL;
|
|
if (node.isObject()) {
|
|
c_SimpleXMLElement *elem =
|
|
node.toObject().getTyped<c_SimpleXMLElement>();
|
|
if (elem->m_node) {
|
|
xmlNodePtr tempnode;
|
|
while ((tempnode = (xmlNodePtr)elem->m_node->children)) {
|
|
xmlUnlinkNode(tempnode);
|
|
}
|
|
elem->m_children = Array::Create();
|
|
change_node_zval(elem->m_node, svalue);
|
|
newnode = elem->m_node;
|
|
}
|
|
} else if (node.isArray()) {
|
|
raise_warning("Cannot assign to an array of nodes "
|
|
"(duplicate subnodes or attr detected)");
|
|
} else if (m_is_attribute) {
|
|
if (name.isInteger()) {
|
|
raise_warning("Cannot change attribute number %" PRId64
|
|
" when only %zd attributes exist", name.toInt64(),
|
|
m_attributes.toArray().size());
|
|
} else {
|
|
newnode = (xmlNodePtr)xmlNewProp(m_node, (xmlChar *)sname.data(), sv);
|
|
}
|
|
} else {
|
|
if (sname.empty() || name.isInteger()) {
|
|
newnode = xmlNewTextChild(m_node->parent, m_node->ns,
|
|
m_node->name, sv);
|
|
} else {
|
|
newnode = xmlNewTextChild(m_node, m_node->ns,
|
|
(xmlChar *)sname.data(), sv);
|
|
}
|
|
}
|
|
|
|
if (newnode) {
|
|
String ns((char*)m_node->ns, CopyString);
|
|
Object child = create_element(m_doc, newnode, ns, false);
|
|
if (m_is_attribute) {
|
|
m_attributes.set(name, child);
|
|
m_children.set(s_attributes, m_attributes);
|
|
} else {
|
|
m_children.set(name, child);
|
|
}
|
|
}
|
|
|
|
return uninit_null();
|
|
}
|
|
|
|
bool c_SimpleXMLElement::o_toBooleanImpl() const noexcept {
|
|
return (m_node || getDynProps().size());
|
|
}
|
|
|
|
int64_t c_SimpleXMLElement::o_toInt64Impl() const noexcept {
|
|
Variant prop;
|
|
ArrayIter iter(m_children.toArray());
|
|
if (iter) {
|
|
prop = iter.second();
|
|
}
|
|
return prop.toString().toInt64();
|
|
}
|
|
|
|
double c_SimpleXMLElement::o_toDoubleImpl() const noexcept {
|
|
Variant prop;
|
|
ArrayIter iter(m_children.toArray());
|
|
if (iter) {
|
|
prop = iter.second();
|
|
}
|
|
return prop.toString().toDouble();
|
|
}
|
|
|
|
Array c_SimpleXMLElement::o_toArray() const {
|
|
if (m_attributes.toArray().empty()) {
|
|
return m_children.toArray();
|
|
}
|
|
Array ret;
|
|
ret.set(s_attributes, m_attributes);
|
|
ret += m_children;
|
|
return ret;
|
|
}
|
|
|
|
Variant c_SimpleXMLElement::t_getiterator() {
|
|
c_SimpleXMLElementIterator *iter = NEWOBJ(c_SimpleXMLElementIterator)();
|
|
iter->set_parent(this);
|
|
return Object(iter);
|
|
}
|
|
|
|
int64_t c_SimpleXMLElement::t_count() {
|
|
if (m_is_attribute) {
|
|
return m_attributes.toArray().size();
|
|
}
|
|
if (m_is_property) {
|
|
int64_t n = 0; Variant var(this);
|
|
for (ArrayIter iter = var.begin(); !iter.end(); iter.next()) {
|
|
++n;
|
|
}
|
|
return n;
|
|
}
|
|
return m_children.toArray().size();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// implementing ArrayAccess
|
|
|
|
bool c_SimpleXMLElement::t_offsetexists(CVarRef index) {
|
|
if (index.isInteger()) {
|
|
int64_t n = 0; int64_t nIndex = index.toInt64(); Variant var(this);
|
|
for (ArrayIter iter = var.begin(); !iter.end(); iter.next()) {
|
|
if (n++ == nIndex) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
return m_attributes.toArray().exists(index);
|
|
}
|
|
|
|
Variant c_SimpleXMLElement::t_offsetget(CVarRef index) {
|
|
if (index.isInteger()) {
|
|
if (m_is_property) {
|
|
int64_t n = 0; int64_t nIndex = index.toInt64(); Variant var(this);
|
|
for (ArrayIter iter = var.begin(); !iter.end(); iter.next()) {
|
|
if (n++ == nIndex) {
|
|
return iter.second();
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
return m_children[index];
|
|
}
|
|
return m_attributes[index];
|
|
}
|
|
|
|
void c_SimpleXMLElement::t_offsetset(CVarRef index, CVarRef newvalue) {
|
|
if (index.isInteger()) {
|
|
raise_error("unable to replace a SimpleXMLElement node");
|
|
return;
|
|
}
|
|
String name = index.toString();
|
|
if (name.empty()) {
|
|
raise_error("cannot create unnamed attribute");
|
|
return;
|
|
}
|
|
|
|
String sv = newvalue.toString();
|
|
if (m_attributes.toArray().exists(name)) {
|
|
t_offsetunset(index);
|
|
}
|
|
|
|
if (m_node == NULL || m_is_text) {
|
|
raise_error("cannot create attribute on this node");
|
|
return;
|
|
}
|
|
xmlNodePtr newnode = (xmlNodePtr)xmlNewProp(m_node, (xmlChar *)name.data(),
|
|
(xmlChar*)sv.data());
|
|
if (newnode) {
|
|
m_attributes.set(name, sv);
|
|
}
|
|
}
|
|
|
|
void c_SimpleXMLElement::t_offsetunset(CVarRef index) {
|
|
if (index.isInteger()) {
|
|
raise_error("unable to remove a SimpleXMLElement node");
|
|
return;
|
|
}
|
|
|
|
String name = index.toString();
|
|
if (name.empty()) {
|
|
raise_error("cannot remove unnamed attribute");
|
|
return;
|
|
}
|
|
|
|
if (m_attributes.toArray().exists(name) && m_node) {
|
|
for (xmlAttrPtr attr = m_node->properties; attr; attr = attr->next) {
|
|
if (String((char*)attr->name, xmlStrlen(attr->name), AttachLiteral) ==
|
|
name) {
|
|
xmlUnlinkNode((xmlNodePtr)attr);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_attributes.remove(name);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
c_SimpleXMLElementIterator::c_SimpleXMLElementIterator(Class* cb) :
|
|
ExtObjectData(cb), m_parent(), m_iter1(NULL), m_iter2(NULL) {
|
|
}
|
|
|
|
c_SimpleXMLElementIterator::~c_SimpleXMLElementIterator() {
|
|
c_SimpleXMLElementIterator::sweep();
|
|
}
|
|
|
|
void c_SimpleXMLElementIterator::sweep() {
|
|
delete m_iter1;
|
|
delete m_iter2;
|
|
}
|
|
|
|
void c_SimpleXMLElementIterator::set_parent(c_SimpleXMLElement* parent) {
|
|
m_parent = parent;
|
|
reset_iterator();
|
|
}
|
|
|
|
void c_SimpleXMLElementIterator::reset_iterator() {
|
|
assert(m_parent.get() != NULL);
|
|
delete m_iter1; m_iter1 = NULL;
|
|
delete m_iter2; m_iter2 = NULL;
|
|
|
|
if (m_parent->m_is_attribute) {
|
|
m_iter1 = new ArrayIter(m_parent->m_attributes.toArray());
|
|
return;
|
|
}
|
|
|
|
// When I'm a node like $node->name, we iterate through all my siblings with
|
|
// same name of mine.
|
|
if (m_parent->m_is_property) {
|
|
String name = m_parent->t_getname();
|
|
Object obj = create_element(m_parent->m_doc, m_parent->m_node->parent,
|
|
"", false);
|
|
m_parent = obj.getTyped<c_SimpleXMLElement>();
|
|
Variant children = m_parent->m_children[name];
|
|
m_parent->m_children = CREATE_MAP1(name, children);
|
|
// fall through
|
|
}
|
|
|
|
if (m_parent->m_is_text) {
|
|
return;
|
|
}
|
|
|
|
if (m_parent->m_children.toArray().size() == 1) {
|
|
ArrayIter iter(m_parent->m_children.toArray());
|
|
if (iter.second().isObject()) {
|
|
c_SimpleXMLElement *elem = iter.second().toObject().
|
|
getTyped<c_SimpleXMLElement>();
|
|
if (elem->m_is_text && elem->m_free_text) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_iter1 = new ArrayIter(m_parent->m_children.toArray());
|
|
if (!m_iter1->end() && m_iter1->second().isArray()) {
|
|
m_iter2 = new ArrayIter(m_iter1->second().toArray());
|
|
}
|
|
}
|
|
|
|
void c_SimpleXMLElementIterator::t___construct() {
|
|
}
|
|
|
|
Variant c_SimpleXMLElementIterator::t_current() {
|
|
if (m_iter1 == NULL) return uninit_null();
|
|
if (m_parent->m_is_attribute) {
|
|
return m_iter1->second();
|
|
}
|
|
|
|
ArrayIter *iter = m_iter2;
|
|
if (iter == NULL && m_iter1->second().isObject()) {
|
|
iter = m_iter1;
|
|
}
|
|
|
|
if (iter) {
|
|
return iter->second();
|
|
}
|
|
|
|
assert(false);
|
|
return uninit_null();
|
|
}
|
|
|
|
Variant c_SimpleXMLElementIterator::t_key() {
|
|
if (m_iter1) {
|
|
return m_iter1->first();
|
|
}
|
|
return uninit_null();
|
|
}
|
|
|
|
Variant c_SimpleXMLElementIterator::t_next() {
|
|
if (m_iter1 == NULL) return uninit_null();
|
|
if (m_parent->m_is_attribute) {
|
|
m_iter1->next();
|
|
return uninit_null();
|
|
}
|
|
|
|
if (m_iter2) {
|
|
m_iter2->next();
|
|
if (!m_iter2->end()) {
|
|
return uninit_null();
|
|
}
|
|
delete m_iter2; m_iter2 = NULL;
|
|
}
|
|
m_iter1->next();
|
|
while (!m_iter1->end()) {
|
|
if (m_iter1->second().isArray()) {
|
|
m_iter2 = new ArrayIter(m_iter1->second().toArray());
|
|
break;
|
|
}
|
|
if (m_iter1->second().isObject()) {
|
|
break;
|
|
}
|
|
m_iter1->next();
|
|
}
|
|
return uninit_null();
|
|
}
|
|
|
|
Variant c_SimpleXMLElementIterator::t_rewind() {
|
|
reset_iterator();
|
|
return uninit_null();
|
|
}
|
|
|
|
Variant c_SimpleXMLElementIterator::t_valid() {
|
|
return m_iter1 && !m_iter1->end();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// LibXMLError
|
|
|
|
c_LibXMLError::c_LibXMLError(Class* cb) :
|
|
ExtObjectData(cb) {
|
|
}
|
|
c_LibXMLError::~c_LibXMLError() {
|
|
}
|
|
void c_LibXMLError::t___construct() {
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// libxml
|
|
|
|
class xmlErrorVec : public std::vector<xmlError> {
|
|
public:
|
|
~xmlErrorVec() {
|
|
reset();
|
|
}
|
|
|
|
void reset() {
|
|
for (unsigned int i = 0; i < size(); i++) {
|
|
xmlResetError(&at(i));
|
|
}
|
|
clear();
|
|
}
|
|
};
|
|
|
|
class LibXmlErrors : public RequestEventHandler {
|
|
public:
|
|
virtual void requestInit() {
|
|
m_use_error = false;
|
|
m_errors.reset();
|
|
xmlParserInputBufferCreateFilenameDefault(NULL);
|
|
}
|
|
virtual void requestShutdown() {
|
|
m_use_error = false;
|
|
m_errors.reset();
|
|
}
|
|
|
|
bool m_use_error;
|
|
xmlErrorVec m_errors;
|
|
};
|
|
|
|
IMPLEMENT_STATIC_REQUEST_LOCAL(LibXmlErrors, s_libxml_errors);
|
|
bool libxml_use_internal_error() {
|
|
return s_libxml_errors->m_use_error;
|
|
}
|
|
extern void libxml_add_error(const std::string &msg) {
|
|
xmlErrorVec *error_list = &s_libxml_errors->m_errors;
|
|
|
|
error_list->resize(error_list->size() + 1);
|
|
xmlError &error_copy = error_list->back();
|
|
memset(&error_copy, 0, sizeof(xmlError));
|
|
|
|
error_copy.domain = 0;
|
|
error_copy.code = XML_ERR_INTERNAL_ERROR;
|
|
error_copy.level = XML_ERR_ERROR;
|
|
error_copy.line = 0;
|
|
error_copy.node = NULL;
|
|
error_copy.int1 = 0;
|
|
error_copy.int2 = 0;
|
|
error_copy.ctxt = NULL;
|
|
error_copy.message = (char*)xmlStrdup((const xmlChar*)msg.c_str());
|
|
error_copy.file = NULL;
|
|
error_copy.str1 = NULL;
|
|
error_copy.str2 = NULL;
|
|
error_copy.str3 = NULL;
|
|
}
|
|
|
|
static void libxml_error_handler(void *userData, xmlErrorPtr error) {
|
|
xmlErrorVec *error_list = &s_libxml_errors->m_errors;
|
|
|
|
error_list->resize(error_list->size() + 1);
|
|
xmlError &error_copy = error_list->back();
|
|
memset(&error_copy, 0, sizeof(xmlError));
|
|
|
|
if (error) {
|
|
xmlCopyError(error, &error_copy);
|
|
} else {
|
|
error_copy.code = XML_ERR_INTERNAL_ERROR;
|
|
error_copy.level = XML_ERR_ERROR;
|
|
}
|
|
}
|
|
|
|
static const StaticString s_level("level");
|
|
static const StaticString s_code("code");
|
|
static const StaticString s_column("column");
|
|
static const StaticString s_message("message");
|
|
static const StaticString s_file("file");
|
|
static const StaticString s_line("line");
|
|
|
|
static Object create_libxmlerror(xmlError &error) {
|
|
Object ret(NEWOBJ(c_LibXMLError)());
|
|
ret->o_set(s_level, error.level);
|
|
ret->o_set(s_code, error.code);
|
|
ret->o_set(s_column, error.int2);
|
|
ret->o_set(s_message, String(error.message, CopyString));
|
|
ret->o_set(s_file, String(error.file, CopyString));
|
|
ret->o_set(s_line, error.line);
|
|
return ret;
|
|
}
|
|
|
|
Variant f_libxml_get_errors() {
|
|
xmlErrorVec *error_list = &s_libxml_errors->m_errors;
|
|
Array ret = Array::Create();
|
|
for (unsigned int i = 0; i < error_list->size(); i++) {
|
|
ret.append(create_libxmlerror(error_list->at(i)));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Variant f_libxml_get_last_error() {
|
|
xmlErrorPtr error = xmlGetLastError();
|
|
if (error) {
|
|
return create_libxmlerror(*error);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void f_libxml_clear_errors() {
|
|
xmlResetLastError();
|
|
s_libxml_errors->m_errors.reset();
|
|
}
|
|
|
|
bool f_libxml_use_internal_errors(CVarRef use_errors /* = null_variant */) {
|
|
bool ret = (xmlStructuredError == libxml_error_handler);
|
|
if (!use_errors.isNull()) {
|
|
if (!use_errors.toBoolean()) {
|
|
xmlSetStructuredErrorFunc(NULL, NULL);
|
|
s_libxml_errors->m_use_error = false;
|
|
s_libxml_errors->m_errors.reset();
|
|
} else {
|
|
xmlSetStructuredErrorFunc(NULL, libxml_error_handler);
|
|
s_libxml_errors->m_use_error = true;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void f_libxml_set_streams_context(CResRef streams_context) {
|
|
throw NotImplementedException(__func__);
|
|
}
|
|
|
|
static xmlParserInputBufferPtr
|
|
hphp_libxml_input_buffer_noload(const char *URI, xmlCharEncoding enc) {
|
|
return NULL;
|
|
}
|
|
|
|
bool f_libxml_disable_entity_loader(bool disable /* = true */) {
|
|
xmlParserInputBufferCreateFilenameFunc old;
|
|
|
|
if (disable) {
|
|
old = xmlParserInputBufferCreateFilenameDefault(hphp_libxml_input_buffer_noload);
|
|
} else {
|
|
old = xmlParserInputBufferCreateFilenameDefault(NULL);
|
|
}
|
|
return (old == hphp_libxml_input_buffer_noload);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|