hphp/runtime/ext/domdocument/ext_domdocument.cpp (5,445 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/ext/domdocument/ext_domdocument.h"
#include <map>
#include "hphp/runtime/base/builtin-functions.h"
#include "hphp/runtime/base/file.h"
#include "hphp/runtime/base/file-util.h"
#include "hphp/runtime/base/runtime-error.h"
#include "hphp/runtime/ext/simplexml/ext_simplexml.h"
#include "hphp/runtime/ext/std/ext_std_classobj.h"
#include "hphp/runtime/ext/std/ext_std_errorfunc.h"
#include "hphp/runtime/ext/std/ext_std_file.h"
#include "hphp/runtime/ext/std/ext_std_function.h"
#include "hphp/runtime/ext/string/ext_string.h"
#include "hphp/runtime/vm/jit/translator-inline.h"
#include "hphp/runtime/vm/native.h"
#include "hphp/system/systemlib.h"
#include "hphp/util/functional.h"
#include "hphp/util/hash-set.h"
#include "hphp/util/string-vsnprintf.h"
#define DOM_XMLNS_NAMESPACE \
(const xmlChar *) "http://www.w3.org/2000/xmlns/"
#define DOM_LOAD_STRING 0
#define DOM_LOAD_FILE 1
#define PHP_DOM_XPATH_QUERY 0
#define PHP_DOM_XPATH_EVALUATE 1
#define DOM_NODESET XML_XINCLUDE_START
namespace HPHP {
IMPLEMENT_DEFAULT_EXTENSION_VERSION(dom, 20031129);
///////////////////////////////////////////////////////////////////////////////
// parser error handling
#define PHP_LIBXML_CTX_ERROR 1
#define PHP_LIBXML_CTX_WARNING 2
#ifndef LIBXML2_NEW_BUFFER
# define xmlOutputBufferGetSize(buf) ((buf)->buffer->use)
# define xmlOutputBufferGetContent(buf) ((buf)->buffer->content)
#endif
// defined in ext_simplexml.cpp
extern xmlNodePtr SimpleXMLElement_exportNode(const Object& sxe);
#define IMPLEMENT_GET_CLASS_FUNCTION(CLASS) \
Class* get ## CLASS ## Class() { \
static Class* cls = Class::lookup(s_ ## CLASS.get()); \
assertx(cls); \
return cls; \
} \
/**/
const StaticString
s_DOMNode("DOMNode"),
s_DOMAttr("DOMAttr"),
s_DOMCharacterData("DOMCharacterData"),
s_DOMComment("DOMComment"),
s_DOMText("DOMText"),
s_DOMCdataSection("DOMCdataSection"),
s_DOMDocument("DOMDocument"),
s_DOMDocumentFragment("DOMDocumentFragment"),
s_DOMDocumentType("DOMDocumentType"),
s_DOMElement("DOMElement"),
s_DOMEntity("DOMEntity"),
s_DOMEntityReference("DOMEntityReference"),
s_DOMNotation("DOMNotation"),
s_DOMProcessingInstruction("DOMProcessingInstruction"),
s_DOMNamedNodeMap("DOMNamedNodeMap"),
s_DOMNodeList("DOMNodeList"),
s_DOMImplementation("DOMImplementation"),
s_DOMXPath("DOMXPath"),
s_DOMNameSpaceNode("DOMNameSpaceNode"),
s_DOMIterable("DOMIterable"),
s_DOMNodeIterator("DOMNodeIterator");
IMPLEMENT_GET_CLASS_FUNCTION(DOMNode)
IMPLEMENT_GET_CLASS_FUNCTION(DOMAttr)
IMPLEMENT_GET_CLASS_FUNCTION(DOMComment)
IMPLEMENT_GET_CLASS_FUNCTION(DOMText)
IMPLEMENT_GET_CLASS_FUNCTION(DOMCdataSection)
IMPLEMENT_GET_CLASS_FUNCTION(DOMDocument)
IMPLEMENT_GET_CLASS_FUNCTION(DOMDocumentFragment)
IMPLEMENT_GET_CLASS_FUNCTION(DOMDocumentType)
IMPLEMENT_GET_CLASS_FUNCTION(DOMElement)
IMPLEMENT_GET_CLASS_FUNCTION(DOMEntity)
IMPLEMENT_GET_CLASS_FUNCTION(DOMEntityReference)
IMPLEMENT_GET_CLASS_FUNCTION(DOMNotation)
IMPLEMENT_GET_CLASS_FUNCTION(DOMProcessingInstruction)
IMPLEMENT_GET_CLASS_FUNCTION(DOMNameSpaceNode)
IMPLEMENT_GET_CLASS_FUNCTION(DOMNodeIterator)
IMPLEMENT_GET_CLASS_FUNCTION(DOMNamedNodeMap)
IMPLEMENT_GET_CLASS_FUNCTION(DOMNodeList)
IMPLEMENT_GET_CLASS_FUNCTION(DOMImplementation)
IMPLEMENT_GET_CLASS_FUNCTION(DOMXPath)
static void php_libxml_internal_error_handler(int error_type, void *ctx,
ATTRIBUTE_PRINTF_STRING const char *fmt,
va_list ap) ATTRIBUTE_PRINTF(3,0);
static void php_libxml_internal_error_handler(int error_type, void *ctx,
const char *fmt,
va_list ap) {
std::string msg;
string_vsnprintf(msg, fmt, ap);
/* remove any trailing \n */
while (!msg.empty() && msg[msg.size() - 1] == '\n') {
msg = msg.substr(0, msg.size() - 1);
}
if (libxml_use_internal_error()) {
libxml_add_error(msg);
return;
}
xmlParserCtxtPtr parser = (xmlParserCtxtPtr) ctx;
if (parser != nullptr && parser->input != nullptr) {
if (parser->input->filename) {
switch (error_type) {
case PHP_LIBXML_CTX_ERROR:
raise_warning("%s in %s, line: %d", msg.c_str(),
parser->input->filename, parser->input->line);
break;
case PHP_LIBXML_CTX_WARNING:
raise_notice("%s in %s, line: %d", msg.c_str(),
parser->input->filename, parser->input->line);
break;
default:
assertx(false);
break;
}
} else {
switch (error_type) {
case PHP_LIBXML_CTX_ERROR:
raise_warning("%s in Entity, line: %d", msg.c_str(),
parser->input->line);
break;
case PHP_LIBXML_CTX_WARNING:
raise_notice("%s in Entity, line: %d", msg.c_str(),
parser->input->line);
break;
default:
assertx(false);
break;
}
}
} else {
switch (error_type) {
case PHP_LIBXML_CTX_ERROR:
raise_warning("%s", msg.c_str());
break;
case PHP_LIBXML_CTX_WARNING:
raise_notice("%s", msg.c_str());
break;
default:
assertx(false);
break;
}
}
}
/**
* The error handler callbacks below are called from libxml code that
* is compiled without frame pointers, so it's necessary to use a
* VMRegGuard before calling libxml code that uses these error handler
* callbacks.
*/
static void php_libxml_ctx_error(void *ctx,
ATTRIBUTE_PRINTF_STRING const char *msg, ...) ATTRIBUTE_PRINTF(2,3);
static void php_libxml_ctx_error(void *ctx,
const char *msg, ...) {
va_list args;
va_start(args, msg);
try {
php_libxml_internal_error_handler(PHP_LIBXML_CTX_ERROR, ctx, msg, args);
} catch (...) {}
va_end(args);
}
static void php_libxml_ctx_warning(void *ctx,
ATTRIBUTE_PRINTF_STRING const char *msg, ...) ATTRIBUTE_PRINTF(2,3);
static void php_libxml_ctx_warning(void *ctx,
const char *msg, ...) {
va_list args;
va_start(args, msg);
try {
php_libxml_internal_error_handler(PHP_LIBXML_CTX_WARNING, ctx, msg, args);
} catch (...) {}
va_end(args);
}
///////////////////////////////////////////////////////////////////////////////
// domexception errors
enum dom_exception_code {
PHP_ERR = 0, // non-spec code for PHP errors
INDEX_SIZE_ERR = 1,
DOMSTRING_SIZE_ERR = 2,
HIERARCHY_REQUEST_ERR = 3,
WRONG_DOCUMENT_ERR = 4,
INVALID_CHARACTER_ERR = 5,
NO_DATA_ALLOWED_ERR = 6,
NO_MODIFICATION_ALLOWED_ERR = 7,
NOT_FOUND_ERR = 8,
NOT_SUPPORTED_ERR = 9,
INUSE_ATTRIBUTE_ERR = 10,
INVALID_STATE_ERR = 11,
SYNTAX_ERR = 12, // Introduced in DOM Level 2
INVALID_MODIFICATION_ERR = 13, // Introduced in DOM Level 2
NAMESPACE_ERR = 14, // Introduced in DOM Level 2
INVALID_ACCESS_ERR = 15, // Introduced in DOM Level 2
VALIDATION_ERR = 16, // Introduced in DOM Level 3
};
static void php_dom_throw_error(dom_exception_code error_code,
bool strict_error) {
const char *error_message;
switch (error_code) {
case INDEX_SIZE_ERR:
error_message = "Index Size Error";
break;
case DOMSTRING_SIZE_ERR:
error_message = "DOM String Size Error";
break;
case HIERARCHY_REQUEST_ERR:
error_message = "Hierarchy Request Error";
break;
case WRONG_DOCUMENT_ERR:
error_message = "Wrong Document Error";
break;
case INVALID_CHARACTER_ERR:
error_message = "Invalid Character Error";
break;
case NO_DATA_ALLOWED_ERR:
error_message = "No Data Allowed Error";
break;
case NO_MODIFICATION_ALLOWED_ERR:
error_message = "No Modification Allowed Error";
break;
case NOT_FOUND_ERR:
error_message = "Not Found Error";
break;
case NOT_SUPPORTED_ERR:
error_message = "Not Supported Error";
break;
case INUSE_ATTRIBUTE_ERR:
error_message = "Inuse Attribute Error";
break;
case INVALID_STATE_ERR:
error_message = "Invalid State Error";
break;
case SYNTAX_ERR:
error_message = "Syntax Error";
break;
case INVALID_MODIFICATION_ERR:
error_message = "Invalid Modification Error";
break;
case NAMESPACE_ERR:
error_message = "Namespace Error";
break;
case INVALID_ACCESS_ERR:
error_message = "Invalid Access Error";
break;
case VALIDATION_ERR:
error_message = "Validation Error";
break;
default:
error_message = "Unhandled Error";
break;
}
if (strict_error) {
SystemLib::throwDOMExceptionObject(error_message);
}
raise_warning(std::string(error_message));
}
static bool dom_has_feature(const char *feature, const char *version) {
bool retval = false;
if (!(strcmp(version, "1.0") && strcmp(version,"2.0") &&
strcmp(version, ""))) {
if ((!strcasecmp(feature, "Core") && !strcmp(version, "1.0")) ||
!strcasecmp(feature, "XML"))
retval = true;
}
return retval;
}
static xmlNode *dom_get_elements_by_tag_name_ns_raw(xmlNodePtr nodep,
const char *ns,
const char *local,
int *cur, int index) {
xmlNodePtr ret = nullptr;
while (nodep != nullptr && (*cur <= index || index == -1)) {
if (nodep->type == XML_ELEMENT_NODE) {
if (xmlStrEqual(nodep->name, (xmlChar *)local) ||
xmlStrEqual((xmlChar *)"*", (xmlChar *)local)) {
if (!ns || !*ns ||
(nodep->ns &&
(xmlStrEqual(nodep->ns->href, (xmlChar *)ns) ||
xmlStrEqual((xmlChar *)"*", (xmlChar *)ns)))) {
if (*cur == index) {
ret = nodep;
break;
}
(*cur)++;
}
}
ret = dom_get_elements_by_tag_name_ns_raw(nodep->children, ns, local,
cur, index);
if (ret != nullptr) {
break;
}
}
nodep = nodep->next;
}
return ret;
}
static void dom_normalize(xmlNodePtr nodep) {
xmlNodePtr nextp, newnextp;
xmlAttrPtr attr;
xmlChar *strContent;
for (xmlNodePtr child = nodep->children; child; child = child->next) {
switch (child->type) {
case XML_TEXT_NODE:
nextp = child->next;
while (nextp != nullptr) {
if (nextp->type == XML_TEXT_NODE) {
newnextp = nextp->next;
strContent = xmlNodeGetContent(nextp);
xmlNodeAddContent(child, strContent);
xmlFree(strContent);
libxml_register_node(nextp)->unlink(); // release nextp if unused
nextp = newnextp;
} else {
break;
}
}
break;
case XML_ELEMENT_NODE:
dom_normalize(child);
attr = child->properties;
while (attr != nullptr) {
dom_normalize((xmlNodePtr)attr);
attr = attr->next;
}
break;
case XML_ATTRIBUTE_NODE:
dom_normalize(child);
break;
default:
break;
}
}
}
const StaticString
s_query("query"),
s_namespaces("namespaces");
static Variant dom_canonicalization(xmlNodePtr nodep, const String& file,
bool exclusive, bool with_comments,
const Variant& xpath_array,
const Variant& ns_prefixes,
int mode) {
xmlDocPtr docp;
xmlNodeSetPtr nodeset = nullptr;
xmlChar **inclusive_ns_prefixes = nullptr;
int ret = -1;
xmlOutputBufferPtr buf;
xmlXPathContextPtr ctxp=nullptr;
xmlXPathObjectPtr xpathobjp=nullptr;
docp = nodep->doc;
if (!docp) {
return false;
}
if (xpath_array.isNull()) {
if (nodep->type != XML_DOCUMENT_NODE) {
ctxp = xmlXPathNewContext(docp);
ctxp->node = nodep;
xpathobjp = xmlXPathEvalExpression
((xmlChar*)"(.//. | .//@* | .//namespace::*)", ctxp);
ctxp->node = nullptr;
if (xpathobjp && xpathobjp->type == XPATH_NODESET) {
nodeset = xpathobjp->nodesetval;
} else {
if (xpathobjp) {
xmlXPathFreeObject(xpathobjp);
}
xmlXPathFreeContext(ctxp);
return false;
}
}
} else {
// xpath query from xpath_array
Array arr = xpath_array.toArray();
String xquery;
if (!arr.exists(s_query)) {
raise_warning("'query' missing from xpath array");
return false;
}
auto const tmp = arr.lookup(s_query);
if (!isStringType(tmp.type())) {
raise_warning("'query' is not a string");
return false;
}
xquery = tvCastToString(tmp);
ctxp = xmlXPathNewContext(docp);
ctxp->node = nodep;
if (arr.exists(s_namespaces)) {
auto const temp = arr.lookup(s_namespaces);
if (isArrayLikeType(temp.type())) {
auto ad = temp.val().parr;
for (ArrayIter it = ArrayIter(ad); it; ++it) {
Variant prefix = it.first();
Variant tmpns = it.second();
if (prefix.isString() || tmpns.isString()) {
xmlXPathRegisterNs(ctxp, (xmlChar*)prefix.toString().data(),
(xmlChar*)tmpns.toString().data());
}
}
}
}
xpathobjp = xmlXPathEvalExpression((xmlChar*)xquery.data(), ctxp);
ctxp->node = nullptr;
if (xpathobjp && xpathobjp->type == XPATH_NODESET) {
nodeset = xpathobjp->nodesetval;
} else {
if (xpathobjp) {
xmlXPathFreeObject(xpathobjp);
}
xmlXPathFreeContext(ctxp);
return false;
}
}
if (!ns_prefixes.isNull()) {
if (exclusive) {
int nscount = 0;
inclusive_ns_prefixes = (xmlChar**)malloc
((ns_prefixes.toArray().size()+1) * sizeof(xmlChar *));
for (ArrayIter it = ns_prefixes.toArray().begin(); it; ++it) {
Variant tmpns = it.second();
if (tmpns.isString()) {
inclusive_ns_prefixes[nscount++] = (xmlChar*)tmpns.toString().data();
}
}
inclusive_ns_prefixes[nscount] = nullptr;
} else {
raise_notice("Inclusive namespace prefixes only allowed in "
"exclusive mode.");
}
}
if (mode == 1) {
buf = xmlOutputBufferCreateFilename(file.c_str(), nullptr, 0);
} else {
buf = xmlAllocOutputBuffer(nullptr);
}
if (buf != nullptr) {
ret = xmlC14NDocSaveTo(docp, nodeset, exclusive, inclusive_ns_prefixes,
with_comments, buf);
}
if (inclusive_ns_prefixes != nullptr) {
free(inclusive_ns_prefixes);
}
if (xpathobjp != nullptr) {
xmlXPathFreeObject(xpathobjp);
}
if (ctxp != nullptr) {
xmlXPathFreeContext(ctxp);
}
Variant retval;
if (buf == nullptr || ret < 0) {
retval = false;
} else {
if (mode == 0) {
ret = xmlOutputBufferGetSize(buf);
if (ret > 0) {
retval = String((char *)xmlOutputBufferGetContent(buf), ret, CopyString);
} else {
retval = empty_string();
}
}
}
if (buf) {
int bytes;
bytes = xmlOutputBufferClose(buf);
if (mode == 1 && (ret >= 0)) {
retval = bytes;
}
}
return retval;
}
static bool dom_node_children_valid(xmlNodePtr node) {
switch (node->type) {
case XML_DOCUMENT_TYPE_NODE:
case XML_DTD_NODE:
case XML_PI_NODE:
case XML_COMMENT_NODE:
case XML_TEXT_NODE:
case XML_CDATA_SECTION_NODE:
case XML_NOTATION_NODE:
return false;
default:
break;
}
return true;
}
static bool dom_node_is_read_only(xmlNodePtr node) {
switch (node->type) {
case XML_ENTITY_REF_NODE:
case XML_ENTITY_NODE:
case XML_DOCUMENT_TYPE_NODE:
case XML_NOTATION_NODE:
case XML_DTD_NODE:
case XML_ELEMENT_DECL:
case XML_ATTRIBUTE_DECL:
case XML_ENTITY_DECL:
case XML_NAMESPACE_DECL:
return true;
default:
break;
}
return node->doc == nullptr;
}
static void dom_set_old_ns(xmlDoc *doc, xmlNs *ns) {
xmlNs *cur;
if (doc == nullptr) return;
if (doc->oldNs == nullptr) {
doc->oldNs = (xmlNsPtr) xmlMalloc(sizeof(xmlNs));
if (doc->oldNs == nullptr) {
return;
}
memset(doc->oldNs, 0, sizeof(xmlNs));
doc->oldNs->type = XML_LOCAL_NAMESPACE;
doc->oldNs->href = xmlStrdup(XML_XML_NAMESPACE);
doc->oldNs->prefix = xmlStrdup((const xmlChar *)"xml");
}
cur = doc->oldNs;
while (cur->next != nullptr) {
cur = cur->next;
}
cur->next = ns;
}
static void dom_reconcile_ns(xmlDocPtr doc, xmlNodePtr nodep) {
xmlNsPtr nsptr, nsdftptr, curns, prevns = nullptr;
if (nodep->type == XML_ELEMENT_NODE) {
// Following if block primarily used for inserting nodes created via
// createElementNS
if (nodep->nsDef != nullptr) {
curns = nodep->nsDef;
while (curns) {
nsdftptr = curns->next;
if (curns->href != nullptr) {
if ((nsptr = xmlSearchNsByHref(doc, nodep->parent, curns->href)) &&
(curns->prefix == nullptr ||
xmlStrEqual(nsptr->prefix, curns->prefix))) {
curns->next = nullptr;
if (prevns == nullptr) {
nodep->nsDef = nsdftptr;
} else {
prevns->next = nsdftptr;
}
dom_set_old_ns(doc, curns);
curns = prevns;
}
}
prevns = curns;
curns = nsdftptr;
}
}
xmlReconciliateNs(doc, nodep);
}
}
static bool dom_hierarchy(xmlNodePtr parent, xmlNodePtr child) {
if (parent == nullptr || child == nullptr || child->doc != parent->doc) {
return true;
}
xmlNodePtr nodep = parent;
while (nodep) {
if (nodep == child) {
return false;
}
nodep = nodep->parent;
}
return true;
}
static xmlNodePtr _php_dom_insert_fragment(xmlNodePtr nodep,
xmlNodePtr prevsib,
xmlNodePtr nextsib,
xmlNodePtr fragment) {
xmlNodePtr newchild, node;
newchild = fragment->children;
if (newchild) {
if (prevsib == nullptr) {
nodep->children = newchild;
} else {
prevsib->next = newchild;
}
newchild->prev = prevsib;
if (nextsib == nullptr) {
nodep->last = fragment->last;
} else {
fragment->last->next = nextsib;
nextsib->prev = fragment->last;
}
node = newchild;
while (node != nullptr) {
node->parent = nodep;
if (node->doc != nodep->doc) {
xmlSetTreeDoc(node, nodep->doc);
}
if (node == fragment->last) {
break;
}
node = node->next;
}
fragment->children = nullptr;
fragment->last = nullptr;
}
return newchild;
}
static int dom_check_qname(const char *qname, char **localname, char **prefix,
int uri_len, int name_len) {
if (name_len == 0) {
return NAMESPACE_ERR;
}
*localname = (char *)xmlSplitQName2((xmlChar *)qname, (xmlChar **) prefix);
if (*localname == nullptr) {
*localname = (char *)xmlStrdup((xmlChar *)qname);
if (*prefix == nullptr && uri_len == 0) {
return 0;
}
}
if (xmlValidateQName((xmlChar *) qname, 0) != 0) {
return NAMESPACE_ERR;
}
if (*prefix != nullptr && uri_len == 0) {
return NAMESPACE_ERR;
}
return 0;
}
static xmlNsPtr dom_get_ns(xmlNodePtr nodep, const char *uri, int *errorcode,
const char *prefix) {
xmlNsPtr nsptr = nullptr;
*errorcode = 0;
if (!((prefix && !strcmp (prefix, "xml") &&
strcmp(uri, (char *)XML_XML_NAMESPACE)) ||
(prefix && !strcmp (prefix, "xmlns") &&
strcmp(uri, (char *)DOM_XMLNS_NAMESPACE)) ||
(prefix && !strcmp(uri, (char *)DOM_XMLNS_NAMESPACE) &&
strcmp (prefix, "xmlns")))) {
nsptr = xmlNewNs(nodep, (xmlChar *)uri, (xmlChar *)prefix);
}
if (nsptr == nullptr) {
*errorcode = NAMESPACE_ERR;
}
return nsptr;
}
static xmlDocPtr dom_document_parser(DOMNode* domnode, bool isFile,
const String& source,
int options) {
VMRegGuard _;
xmlDocPtr ret = nullptr;
xmlParserCtxtPtr ctxt = nullptr;
auto domdoc = domnode->doc();
bool validate, recover, resolve_externals, keep_blanks, substitute_ent;
validate = domdoc->m_validateonparse;
resolve_externals = domdoc->m_resolveexternals;
keep_blanks = domdoc->m_preservewhitespace;
substitute_ent = domdoc->m_substituteentities;
recover = domdoc->m_recover;
req::ptr<File> stream;
if (isFile) {
String file_dest = libxml_get_valid_file_path(source);
if (!file_dest.empty()) {
// This is considerably more verbose than just using
// xmlCreateFileParserCtxt, but it allows us to bypass the external
// entity loading path, which is locked down by default for security
// reasons.
stream = File::Open(file_dest, "rb");
if (stream && !stream->isInvalid()) {
// 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.
ctxt = xmlCreateIOParserCtxt(nullptr, nullptr,
libxml_streams_IO_read,
libxml_streams_IO_nop_close,
&stream,
XML_CHAR_ENCODING_NONE);
}
}
} else {
ctxt = xmlCreateMemoryParserCtxt(source.data(), source.size());
}
if (ctxt == nullptr) return nullptr;
/* If loading from memory, we need to set the base directory for the
* document */
if (!isFile) {
String directory = g_context->getCwd();
if (!directory.empty()) {
if (ctxt->directory != nullptr) xmlFree(ctxt->directory);
if (directory[directory.size() - 1] != '/') directory += "/";
ctxt->directory =
(char*)xmlCanonicPath((const xmlChar*)directory.c_str());
}
}
ctxt->vctxt.error = php_libxml_ctx_error;
ctxt->vctxt.warning = php_libxml_ctx_warning;
if (ctxt->sax != nullptr) {
ctxt->sax->error = php_libxml_ctx_error;
ctxt->sax->warning = php_libxml_ctx_warning;
}
if (validate && ! (options & XML_PARSE_DTDVALID)) {
options |= XML_PARSE_DTDVALID;
}
if (resolve_externals && ! (options & XML_PARSE_DTDATTR)) {
options |= XML_PARSE_DTDATTR;
}
if (substitute_ent && ! (options & XML_PARSE_NOENT)) {
options |= XML_PARSE_NOENT;
}
if (keep_blanks == 0 && ! (options & XML_PARSE_NOBLANKS)) {
options |= XML_PARSE_NOBLANKS;
}
xmlCtxtUseOptions(ctxt, options);
ctxt->recovery = recover;
int old_error_reporting = 0;
if (recover) {
old_error_reporting = HHVM_FN(error_reporting)();
HHVM_FN(error_reporting)(old_error_reporting | (int)ErrorMode::WARNING);
}
xmlParseDocument(ctxt);
if (ctxt->wellFormed || recover) {
ret = ctxt->myDoc;
if (ctxt->recovery) {
HHVM_FN(error_reporting)(old_error_reporting);
}
if (ret && ret->URL == nullptr) {
if (isFile) {
ret->URL = xmlStrdup((xmlChar*)source.c_str());
} else {
/* If loading from memory, set the base reference uri for the
* document */
if (ctxt->directory != nullptr) {
ret->URL = xmlStrdup((xmlChar*)ctxt->directory);
}
}
}
} else {
ret = nullptr;
xmlFreeDoc(ctxt->myDoc);
ctxt->myDoc = nullptr;
}
xmlFreeParserCtxt(ctxt);
return ret;
}
namespace {
DOMNode* getDOMNode(const Object& node) {
if (!node.instanceof(getDOMNodeClass())) {
SystemLib::throwInvalidArgumentExceptionObject(
folly::sformat(
"Invalid object. Expected DOMNode, received {}",
node->getClassName().c_str()
)
);
}
return Native::data<DOMNode>(node);
}
DOMXPath* getDOMXPath(const Object& obj) {
if (!obj.instanceof(getDOMXPathClass())) {
SystemLib::throwInvalidArgumentExceptionObject(
folly::sformat(
"Invalid object. Expected DOMXPath, received {}",
obj->getClassName().c_str()
)
);
}
return Native::data<DOMXPath>(obj);
}
}
static bool HHVM_METHOD(DomDocument, _load, const String& source,
int64_t options, bool isFile) {
if (source.empty()) {
raise_warning("Empty string supplied as input");
return false;
}
if (isFile && !FileUtil::isValidPath(source)) {
raise_warning("Invalid file source");
return false;
}
auto domdoc = Native::data<DOMNode>(this_);
auto newdoc = dom_document_parser(domdoc, isFile, source, options);
if (!newdoc) {
return false;
}
auto olddoc = domdoc->node() ? domdoc->doc() : nullptr;
domdoc->setNode((xmlNodePtr)newdoc);
if (olddoc) {
domdoc->doc()->copyProperties(olddoc);
}
return true;
}
static bool HHVM_METHOD(DomDocument, _loadHTML, const String& source,
int64_t options, bool isFile) {
VMRegGuard _;
if (source.empty()) {
raise_warning("Empty string supplied as input");
return false;
}
htmlParserCtxtPtr ctxt;
if (isFile) {
if (!FileUtil::isValidPath(source)) {
raise_warning("Invalid file source");
return false;
}
ctxt = htmlCreateFileParserCtxt(source.data(), nullptr);
} else {
ctxt = htmlCreateMemoryParserCtxt(source.data(), source.size());
}
if (!ctxt) {
return false;
}
ctxt->vctxt.error = php_libxml_ctx_error;
ctxt->vctxt.warning = php_libxml_ctx_warning;
if (ctxt->sax != nullptr) {
ctxt->sax->error = php_libxml_ctx_error;
ctxt->sax->warning = php_libxml_ctx_warning;
}
if (options) {
htmlCtxtUseOptions(ctxt, options);
}
htmlParseDocument(ctxt);
xmlDocPtr newdoc = ctxt->myDoc;
htmlFreeParserCtxt(ctxt);
if (!newdoc) {
return false;
}
auto domdoc = Native::data<DOMNode>(this_);
auto olddoc = domdoc->node() ? domdoc->doc() : nullptr;
domdoc->setNode((xmlNodePtr)newdoc);
if (olddoc) {
domdoc->doc()->copyProperties(olddoc);
}
return true;
}
static bool _dom_document_relaxNG_validate(DOMNode* domdoc,
const String& source, int type) {
xmlRelaxNGParserCtxtPtr parser;
xmlRelaxNGPtr sptr;
xmlRelaxNGValidCtxtPtr vptr;
int is_valid;
if (source.empty()) {
raise_warning("Invalid Schema source");
return false;
}
xmlDocPtr docp = (xmlDocPtr)domdoc->nodep();
switch (type) {
case DOM_LOAD_FILE:
{
String valid_file = libxml_get_valid_file_path(source.data());
if (valid_file.empty()) {
raise_warning("Invalid RelaxNG file source");
return false;
}
parser = xmlRelaxNGNewParserCtxt(valid_file.data());
}
break;
case DOM_LOAD_STRING:
parser = xmlRelaxNGNewMemParserCtxt(source.data(), source.size());
/* If loading from memory, we need to set the base directory for
the document. but it is not apparent how to do that for schema's */
break;
default:
return false;
}
xmlRelaxNGSetParserErrors
(parser, (xmlRelaxNGValidityErrorFunc) php_libxml_ctx_error,
(xmlRelaxNGValidityWarningFunc) php_libxml_ctx_error, nullptr);
sptr = xmlRelaxNGParse(parser);
xmlRelaxNGFreeParserCtxt(parser);
if (!sptr) {
raise_warning("Invalid RelaxNG");
return false;
}
vptr = xmlRelaxNGNewValidCtxt(sptr);
if (!vptr) {
xmlRelaxNGFree(sptr);
raise_error("Invalid RelaxNG Validation Context");
return false;
}
xmlRelaxNGSetValidErrors(vptr, php_libxml_ctx_error,
php_libxml_ctx_error, nullptr);
is_valid = xmlRelaxNGValidateDoc(vptr, docp);
xmlRelaxNGFree(sptr);
xmlRelaxNGFreeValidCtxt(vptr);
return is_valid == 0;
}
static bool _dom_document_schema_validate(DOMNode* domdoc,
const String& source, int type) {
xmlDocPtr docp = (xmlDocPtr)domdoc->nodep();
xmlSchemaParserCtxtPtr parser;
xmlSchemaPtr sptr;
xmlSchemaValidCtxtPtr vptr;
int is_valid;
if (source.empty()) {
raise_warning("Invalid Schema source");
return false;
}
switch (type) {
case DOM_LOAD_FILE:
{
String valid_file = libxml_get_valid_file_path(source.data());
if (valid_file.empty()) {
raise_warning("Invalid Schema file source");
return false;
}
parser = xmlSchemaNewParserCtxt(valid_file.data());
}
break;
case DOM_LOAD_STRING:
parser = xmlSchemaNewMemParserCtxt(source.data(), source.size());
/* If loading from memory, we need to set the base directory for
the document. but it is not apparent how to do that for schema's */
break;
default:
return false;
}
xmlSchemaSetParserErrors
(parser, (xmlSchemaValidityErrorFunc) php_libxml_ctx_error,
(xmlSchemaValidityWarningFunc) php_libxml_ctx_error, nullptr);
sptr = xmlSchemaParse(parser);
xmlSchemaFreeParserCtxt(parser);
if (!sptr) {
raise_warning("Invalid Schema");
return false;
}
vptr = xmlSchemaNewValidCtxt(sptr);
if (!vptr) {
xmlSchemaFree(sptr);
raise_error("Invalid Schema Validation Context");
return false;
}
xmlSchemaSetValidErrors(vptr, php_libxml_ctx_error,
php_libxml_ctx_error, nullptr);
is_valid = xmlSchemaValidateDoc(vptr, docp);
xmlSchemaFree(sptr);
xmlSchemaFreeValidCtxt(vptr);
return is_valid == 0;
}
static xmlNodePtr php_dom_free_xinclude_node(xmlNodePtr cur) {
xmlNodePtr xincnode;
xincnode = cur;
cur = cur->next;
libxml_register_node(xincnode)->unlink(); // release xincnode if unused
return cur;
}
void php_dom_remove_xinclude_nodes(xmlNodePtr cur) {
while (cur) {
if (cur->type == XML_XINCLUDE_START) {
cur = php_dom_free_xinclude_node(cur);
/* XML_XINCLUDE_END node will be a sibling of XML_XINCLUDE_START */
while (cur && cur->type != XML_XINCLUDE_END) {
/* remove xinclude processing nodes from recursive xincludes */
if (cur->type == XML_ELEMENT_NODE) {
php_dom_remove_xinclude_nodes(cur->children);
}
cur = cur->next;
}
if (cur && cur->type == XML_XINCLUDE_END) {
cur = php_dom_free_xinclude_node(cur);
}
} else {
if (cur->type == XML_ELEMENT_NODE) {
php_dom_remove_xinclude_nodes(cur->children);
}
cur = cur->next;
}
}
}
static void php_dom_xmlSetTreeDoc(xmlNodePtr tree, xmlDocPtr doc) {
xmlAttrPtr prop;
xmlNodePtr cur;
if (tree) {
if (tree->type == XML_ELEMENT_NODE) {
prop = tree->properties;
while (prop != nullptr) {
prop->doc = doc;
if (prop->children) {
cur = prop->children;
while (cur != nullptr) {
php_dom_xmlSetTreeDoc(cur, doc);
cur = cur->next;
}
}
prop = prop->next;
}
}
if (tree->children != nullptr) {
cur = tree->children;
while (cur != nullptr) {
php_dom_xmlSetTreeDoc(cur, doc);
cur = cur->next;
}
}
tree->doc = doc;
}
}
static xmlNodePtr dom_get_dom1_attribute(xmlNodePtr elem, xmlChar *name) {
int len;
const xmlChar *nqname;
nqname = xmlSplitQName3(name, &len);
if (nqname != nullptr) {
xmlNsPtr ns;
xmlChar *prefix = xmlStrndup(name, len);
if (prefix && xmlStrEqual(prefix, (xmlChar*)"xmlns")) {
ns = elem->nsDef;
while (ns) {
if (xmlStrEqual(ns->prefix, nqname)) {
break;
}
ns = ns->next;
}
xmlFree(prefix);
return (xmlNodePtr)ns;
}
ns = xmlSearchNs(elem->doc, elem, prefix);
if (prefix != nullptr) {
xmlFree(prefix);
}
if (ns != nullptr) {
return (xmlNodePtr)xmlHasNsProp(elem, nqname, ns->href);
}
} else {
if (xmlStrEqual(name, (xmlChar*)"xmlns")) {
xmlNsPtr nsPtr = elem->nsDef;
while (nsPtr) {
if (nsPtr->prefix == nullptr) {
return (xmlNodePtr)nsPtr;
}
nsPtr = nsPtr->next;
}
return nullptr;
}
}
return (xmlNodePtr)xmlHasNsProp(elem, name, nullptr);
}
static xmlNsPtr dom_get_nsdecl(xmlNode *node, xmlChar *localName) {
xmlNsPtr cur;
xmlNs *ret = nullptr;
if (node == nullptr) {
return nullptr;
}
if (localName == nullptr || xmlStrEqual(localName, (xmlChar *)"")) {
cur = node->nsDef;
while (cur != nullptr) {
if (cur->prefix == nullptr && cur->href != nullptr) {
ret = cur;
break;
}
cur = cur->next;
}
} else {
cur = node->nsDef;
while (cur != nullptr) {
if (cur->prefix != nullptr && xmlStrEqual(localName, cur->prefix)) {
ret = cur;
break;
}
cur = cur->next;
}
}
return ret;
}
// These need to be lowercase...
const StaticString
s_domdocument("domdocument"),
s_domdocumenttype("domdocumenttype"),
s_domelement("domelement"),
s_domattr("domattr"),
s_domtext("domtext"),
s_domcomment("domcomment"),
s_domprocessinginstruction("domprocessinginstruction"),
s_domentityreference("domentityreference"),
s_domentity("domentity"),
s_domcdatasection("domcdatasection"),
s_domdocumentfragment("domdocumentfragment"),
s_domnotation("domnotation"),
s_domnamespacenode("domnamespacenode");
static String domClassname(xmlNodePtr obj) {
switch (obj->type) {
case XML_DOCUMENT_NODE:
case XML_HTML_DOCUMENT_NODE: return s_domdocument;
case XML_DTD_NODE:
case XML_DOCUMENT_TYPE_NODE: return s_domdocumenttype;
case XML_ELEMENT_NODE: return s_domelement;
case XML_ATTRIBUTE_NODE: return s_domattr;
case XML_TEXT_NODE: return s_domtext;
case XML_COMMENT_NODE: return s_domcomment;
case XML_PI_NODE: return s_domprocessinginstruction;
case XML_ENTITY_REF_NODE: return s_domentityreference;
case XML_ENTITY_DECL:
case XML_ELEMENT_DECL: return s_domentity;
case XML_CDATA_SECTION_NODE: return s_domcdatasection;
case XML_DOCUMENT_FRAG_NODE: return s_domdocumentfragment;
case XML_NOTATION_NODE: return s_domnotation;
case XML_NAMESPACE_DECL: return s_domnamespacenode;
default:
return String((StringData*)nullptr);
}
}
Variant php_dom_create_object(xmlNodePtr obj,
req::ptr<XMLDocumentData> doc) {
String clsname = domClassname(obj);
if (!clsname) {
raise_warning("Unsupported node type: %d", obj->type);
return init_null();
}
if (doc) {
if (doc->m_classmap.exists(clsname)) {
// or const char * is not safe
assertx(doc->m_classmap[clsname].isString());
clsname = doc->m_classmap[clsname].toString();
}
}
auto node = libxml_register_node(obj);
Object od = node->getCache()
? Object{node->getCache()}
: Object::attach(g_context->createObjectOnly(clsname.get()));
auto* nodeobj = Native::data<DOMNode>(od);
if (!nodeobj->node()) {
nodeobj->setNode(node);
}
assertx(nodeobj->node() == node);
if (doc) {
nodeobj->setDoc(std::move(doc));
}
return od;
}
static Variant create_node_object(xmlNodePtr node,
req::ptr<XMLDocumentData> doc) {
if (!node) {
return init_null();
}
Variant retval = php_dom_create_object(node, doc);
if (retval.isNull()) {
raise_warning("Cannot create required DOM object");
}
return retval;
}
static Variant create_node_object(xmlNodePtr obj) {
return create_node_object(obj, req::ptr<XMLDocumentData>(nullptr));
}
static Variant create_node_object(xmlNodePtr obj, Object docObj) {
if (docObj) {
auto doc = getDOMNode(docObj);
return create_node_object(obj, doc->doc());
}
return create_node_object(obj);
}
static Variant php_xpath_eval(DOMXPath* domxpath, const String& expr,
const Object& context, int type,
bool registerNodeNS) {
xmlXPathObjectPtr xpathobjp;
int nsnbr = 0, xpath_type;
xmlDoc *docp = nullptr;
xmlNsPtr *ns = nullptr;
xmlXPathContextPtr ctxp = (xmlXPathContextPtr)domxpath->m_node;
if (ctxp == nullptr) {
raise_warning("Invalid XPath Context");
return false;
}
docp = (xmlDocPtr)ctxp->doc;
if (docp == nullptr) {
raise_warning("Invalid XPath Document Pointer");
return false;
}
xmlNodePtr nodep = nullptr;
if (!context.isNull()) {
DOMNode *domnode = getDOMNode(context);
nodep = domnode->nodep();
}
if (!nodep) {
nodep = xmlDocGetRootElement(docp);
}
if (nodep && docp != nodep->doc) {
raise_warning("Node From Wrong Document");
return false;
}
ctxp->node = nodep;
/* Register namespaces in the node */
if (registerNodeNS) {
ns = xmlGetNsList(docp, nodep);
if (ns != nullptr) {
while (ns[nsnbr] != nullptr) nsnbr++;
}
}
ctxp->namespaces = ns;
ctxp->nsNr = nsnbr;
checkVMRegStateGuarded();
xpathobjp = xmlXPathEvalExpression((xmlChar*)expr.data(), ctxp);
ctxp->node = nullptr;
if (ns != nullptr) {
xmlFree(ns);
ctxp->namespaces = nullptr;
ctxp->nsNr = 0;
}
if (!xpathobjp) {
return false;
}
if (type == PHP_DOM_XPATH_QUERY) {
xpath_type = XPATH_NODESET;
} else {
xpath_type = xpathobjp->type;
}
Variant ret;
switch (xpath_type) {
case XPATH_NODESET:
{
int i;
xmlNodeSetPtr nodesetp;
Array retval = Array::CreateVec();
if (xpathobjp->type == XPATH_NODESET &&
nullptr != (nodesetp = xpathobjp->nodesetval)) {
for (i = 0; i < nodesetp->nodeNr; i++) {
xmlNodePtr node = nodesetp->nodeTab[i];
if (node->type == XML_NAMESPACE_DECL) {
xmlNsPtr curns;
//xmlNodePtr nsparent;
//nsparent = node->_private;
curns = xmlNewNs(nullptr, node->name, nullptr);
if (node->children) {
curns->prefix = xmlStrdup((xmlChar*)node->children);
}
if (node->children) {
node = xmlNewDocNode(docp, nullptr, (xmlChar*)node->children,
node->name);
} else {
node = xmlNewDocNode(docp, nullptr, (xmlChar*)"xmlns", node->name);
}
node->type = XML_NAMESPACE_DECL;
//node->parent = nsparent;
node->ns = curns;
}
// Ugh, the statement below creates a new object and
// adds it to an array...
retval.append(create_node_object(node, domxpath->m_doc));
}
}
auto docData = Native::data<DOMNode>(domxpath->m_doc)->doc();
Object node_list{getDOMNodeListClass()};
auto list_data = Native::data<DOMIterable>(node_list);
list_data->m_doc = docData;
list_data->m_baseobjptr = retval;
list_data->m_nodetype = DOM_NODESET;
ret = std::move(node_list);
break;
}
case XPATH_BOOLEAN:
ret = (bool)(xpathobjp->boolval);
break;
case XPATH_NUMBER:
ret = (double)(xpathobjp->floatval);
break;
case XPATH_STRING:
ret = String((char*)xpathobjp->stringval, CopyString);
break;
default:
ret = uninit_null();
break;
}
xmlXPathFreeObject(xpathobjp);
return ret;
}
static void php_set_attribute_id(xmlAttrPtr attrp, bool is_id) {
if (is_id == 1 && attrp->atype != XML_ATTRIBUTE_ID) {
xmlChar *id_val = xmlNodeListGetString(attrp->doc, attrp->children, 1);
if (id_val != nullptr) {
xmlAddID(nullptr, attrp->doc, id_val, attrp);
xmlFree(id_val);
}
} else {
if (attrp->atype == XML_ATTRIBUTE_ID) {
xmlRemoveID(attrp->doc, attrp);
attrp->atype = (xmlAttributeType)0;
}
}
}
static xmlNsPtr _dom_new_reconNs(xmlDocPtr doc, xmlNodePtr tree, xmlNsPtr ns) {
xmlNsPtr def;
xmlChar prefix[50];
int counter = 1;
if (tree == nullptr || ns == nullptr || ns->type != XML_NAMESPACE_DECL) {
return nullptr;
}
/* Code taken from libxml2 (2.6.20) xmlNewReconciliedNs
*
* Find a close prefix which is not already in use.
* Let's strip namespace prefixes longer than 20 chars!
*/
if (ns->prefix == nullptr) {
snprintf((char*)prefix, sizeof(prefix), "default");
} else {
snprintf((char*)prefix, sizeof(prefix), "%.20s", (char *)ns->prefix);
}
def = xmlSearchNs(doc, tree, prefix);
while (def != nullptr) {
if (counter > 1000) return(nullptr);
if (ns->prefix == nullptr)
snprintf((char*)prefix, sizeof(prefix), "default%d", counter++);
else
snprintf((char*)prefix, sizeof(prefix), "%.20s%d",
(char*)ns->prefix, counter++);
def = xmlSearchNs(doc, tree, prefix);
}
/*
* OK, now we are ready to create a new one.
*/
def = xmlNewNs(tree, ns->href, prefix);
return(def);
}
static const char * getStringData(const String& str) {
if (str.isNull()) {
return nullptr;
}
return str.data();
}
static xmlNodePtr create_notation(const xmlChar *name,
const xmlChar *ExternalID,
const xmlChar *SystemID) {
xmlEntityPtr ret;
ret = (xmlEntityPtr) xmlMalloc(sizeof(xmlEntity));
memset(ret, 0, sizeof(xmlEntity));
ret->type = XML_NOTATION_NODE;
ret->name = xmlStrdup(name);
ret->ExternalID = xmlStrdup(ExternalID);
ret->SystemID = xmlStrdup(SystemID);
ret->length = 0;
ret->content = nullptr;
ret->URI = nullptr;
ret->orig = nullptr;
ret->children = nullptr;
ret->parent = nullptr;
ret->doc = nullptr;
ret->_private = nullptr;
ret->last = nullptr;
ret->prev = nullptr;
return((xmlNodePtr) ret);
}
struct nodeIterator {
int cur;
int index;
xmlNode *node;
};
struct notationIterator {
int cur;
int index;
xmlNotation *notation;
};
#if LIBXML_VERSION >= 20908
#define XMLCHAR_CONST const
#else
#define XMLCHAR_CONST
#endif
static void itemHashScanner(void* payload, void* data, XMLCHAR_CONST xmlChar* /*name*/) {
#undef XMLCHAR_CONST
nodeIterator *priv = (nodeIterator *)data;
if (priv->cur < priv->index) {
priv->cur++;
} else if (priv->node == nullptr) {
priv->node = (xmlNode *)payload;
}
}
static xmlNode *php_dom_libxml_hash_iter(xmlHashTable *ht, int index) {
int htsize = xmlHashSize(ht);
if (htsize > 0 && index < htsize) {
nodeIterator iter;
iter.cur = 0;
iter.index = index;
iter.node = nullptr;
xmlHashScan(ht, itemHashScanner, &iter);
return iter.node;
}
return nullptr;
}
static xmlNode *php_dom_libxml_notation_iter(xmlHashTable *ht, int index) {
int htsize = xmlHashSize(ht);
if (htsize > 0 && index < htsize) {
notationIterator iter;
iter.cur = 0;
iter.index = index;
iter.notation = nullptr;
xmlHashScan(ht, itemHashScanner, &iter);
xmlNotation *notep = iter.notation;
return create_notation(notep->name, notep->PublicID, notep->SystemID);
}
return nullptr;
}
///////////////////////////////////////////////////////////////////////////////
Variant dummy_getter(const Object&) {
raise_notice("Cannot read property");
return init_null();
}
void dummy_setter(const Object&, const Variant&) {
raise_error("Cannot write property");
}
struct DOMPropertyAccessor {
const char * name;
Variant (*getter)(const Object&);
void (*setter)(const Object&, const Variant&);
};
const StaticString s_object_value_omitted("(object value omitted)");
struct hashdpa {
size_t operator()(const DOMPropertyAccessor* da) const {
return hash_string_i(da->name, strlen(da->name));
}
};
struct cmpdpa {
bool operator()(const DOMPropertyAccessor* da1,
const DOMPropertyAccessor* da2) const {
return strcasecmp(da1->name, da2->name) == 0;
}
};
///////////////////////////////////////////////////////////////////////////////
/**
* Base native property handler class, that is reused
* by derived DOM classes with the similar handling.
*/
template <class Derived>
struct DOMPropHandler: Native::BasePropHandler {
static Variant getProp(const Object& this_, const String& name) {
return Derived::map.getter(name)(this_);
}
static Variant setProp(const Object& this_,
const String& name,
const Variant& value) {
Derived::map.setter(name)(this_, value);
return true;
}
static Variant issetProp(const Object& this_, const String& name) {
return Derived::map.isset(this_, name);
}
static bool isPropSupported(const String& name, const String& /*op*/) {
return Derived::map.isPropertySupported(name);
}
};
///////////////////////////////////////////////////////////////////////////////
struct DOMPropertyAccessorMap :
private hphp_hash_set<DOMPropertyAccessor*, hashdpa, cmpdpa> {
explicit DOMPropertyAccessorMap(DOMPropertyAccessor* props,
DOMPropertyAccessorMap *base = nullptr) {
if (base) {
*this = *base;
}
for (DOMPropertyAccessor *p = props; p->name; p++) {
this->insert(p);
m_props.push_back(p);
}
}
bool isPropertySupported(const String& name) {
auto dpa = DOMPropertyAccessor {
name.data(), nullptr, nullptr
};
const_iterator iter = find(&dpa);
return iter != end();
}
Variant (*getter(const String& name))(const Object&) {
auto dpa = DOMPropertyAccessor {
name.data(), nullptr, nullptr
};
const_iterator iter = find(&dpa);
if (iter != end() && (*iter)->getter) {
if (strcmp(dpa.name, (*iter)->name)) {
raise_warning("Accessing DOMNode derived property '%s' with the "
"incorrect casing", dpa.name);
}
return (*iter)->getter;
}
return dummy_getter;
}
void (*setter(const String& name))(const Object&, const Variant&) {
auto dpa = DOMPropertyAccessor {
name.data(), nullptr, nullptr
};
const_iterator iter = find(&dpa);
if (iter != end() && (*iter)->setter) {
if (strcmp(dpa.name, (*iter)->name)) {
raise_warning("Setting DOMNode derived property '%s' with the "
"incorrect casing", dpa.name);
}
return (*iter)->setter;
}
return dummy_setter;
}
bool isset(const Object& obj, const String& name) {
auto dpa = DOMPropertyAccessor {
name.data(), nullptr, nullptr
};
const_iterator iter = find(&dpa);
if (iter != end() && (*iter)->getter) {
if (strcmp(dpa.name, (*iter)->name)) {
raise_warning("Accessing DOMNode derived property '%s' with the "
"incorrect casing", dpa.name);
}
return !(*iter)->getter(obj).isNull();
}
return false;
}
Array debugInfo(const Object& obj) {
auto ret = obj->toArray();
for (auto it : m_props) {
auto value = it->getter(obj);
if (value.isObject()) {
value = s_object_value_omitted;
}
ret.set(String(it->name, CopyString), value);
}
return ret;
}
private:
std::vector<DOMPropertyAccessor*> m_props;
};
///////////////////////////////////////////////////////////////////////////////
void HHVM_METHOD(DOMDocument, __construct,
const Variant& version /* = null_string */,
const Variant& encoding /* = null_string */);
Object newDOMDocument(bool construct /* = true */) {
Object doc{getDOMDocumentClass()};
if (LIKELY(construct)) {
HHVM_MN(DOMDocument, __construct)(doc.get(), null_string, null_string);
}
return doc;
}
static Object newDOMNamedNodeMap(req::ptr<XMLDocumentData> doc, Object base,
int node_type, xmlHashTable* ht = nullptr) {
Object nodemap{getDOMNamedNodeMapClass()};
auto data = Native::data<DOMIterable>(nodemap);
data->m_doc = doc;
data->m_baseobj = base;
data->m_nodetype = node_type;
data->m_ht = ht;
return nodemap;
}
static Object newDOMNodeList(req::ptr<XMLDocumentData> doc, Object base,
int node_type, String local = String(),
String ns = String()) {
Object ret{getDOMNodeListClass()};
auto data = Native::data<DOMIterable>(ret);
data->m_doc = doc;
data->m_baseobj = base;
data->m_nodetype = node_type;
data->m_local = local;
data->m_ns = ns;
return ret;
}
#define CHECK_NODE(nodepVar) \
DOMNode *domnode = getDOMNode(obj); \
xmlNodePtr nodepVar = domnode->node() ? domnode->nodep() : nullptr; \
if (nodepVar == nullptr) { \
php_dom_throw_error(INVALID_STATE_ERR, 0); \
return init_null(); \
} \
/**/
#define CHECK_WRITE_NODE(nodepVar) \
DOMNode *domnode = getDOMNode(obj); \
xmlNodePtr nodepVar = domnode->node() ? domnode->nodep() : nullptr; \
if (nodepVar == nullptr) { \
php_dom_throw_error(INVALID_STATE_ERR, 0); \
return; \
} \
/**/
static Variant domnode_nodename_read(const Object& obj) {
CHECK_NODE(nodep);
xmlNsPtr ns;
char *str = nullptr;
xmlChar *qname = nullptr;
switch (nodep->type) {
case XML_ATTRIBUTE_NODE:
case XML_ELEMENT_NODE:
ns = nodep->ns;
if (ns != nullptr && ns->prefix) {
qname = xmlStrdup(ns->prefix);
qname = xmlStrcat(qname, (const xmlChar*)":");
qname = xmlStrcat(qname, nodep->name);
str = (char*)qname;
} else {
str = (char*)nodep->name;
}
break;
case XML_NAMESPACE_DECL:
ns = nodep->ns;
if (ns != nullptr && ns->prefix) {
qname = xmlStrdup((const xmlChar*)"xmlns");
qname = xmlStrcat(qname, (const xmlChar*)":");
qname = xmlStrcat(qname, nodep->name);
str = (char*)qname;
} else {
str = (char*)nodep->name;
}
break;
case XML_DOCUMENT_TYPE_NODE:
case XML_DTD_NODE:
case XML_PI_NODE:
case XML_ENTITY_DECL:
case XML_ENTITY_REF_NODE:
case XML_NOTATION_NODE:
str = (char*)nodep->name;
break;
case XML_CDATA_SECTION_NODE: return "#cdata-section";
case XML_COMMENT_NODE: return "#comment";
case XML_HTML_DOCUMENT_NODE:
case XML_DOCUMENT_NODE: return "#document";
case XML_DOCUMENT_FRAG_NODE: return "#document-fragment";
case XML_TEXT_NODE: return "#text";
default:
raise_warning("Invalid Node Type");
break;
}
String retval(str, CopyString);
if (qname != nullptr) {
xmlFree(qname);
}
return retval;
}
static Variant domnode_nodevalue_read(const Object& obj) {
CHECK_NODE(nodep);
char *str = nullptr;
/* Access to Element node is implemented as a convience method */
switch (nodep->type) {
case XML_ATTRIBUTE_NODE:
case XML_TEXT_NODE:
case XML_ELEMENT_NODE:
case XML_COMMENT_NODE:
case XML_CDATA_SECTION_NODE:
case XML_PI_NODE:
str = (char*)xmlNodeGetContent(nodep);
break;
case XML_NAMESPACE_DECL:
str = (char*)xmlNodeGetContent(nodep->children);
break;
default:
str = nullptr;
break;
}
if (str != nullptr) {
String retval(str, CopyString);
xmlFree(str);
return retval;
} else {
return init_null();
}
}
static void domnode_nodevalue_write(const Object& obj, const Variant& value) {
CHECK_WRITE_NODE(nodep);
/* Access to Element node is implemented as a convience method */
switch (nodep->type) {
case XML_ELEMENT_NODE:
case XML_ATTRIBUTE_NODE:
if (nodep->children) {
php_libxml_node_free_resource((xmlNodePtr) nodep->children);
nodep->children = nullptr;
}
case XML_TEXT_NODE:
case XML_COMMENT_NODE:
case XML_CDATA_SECTION_NODE:
case XML_PI_NODE:
{
String valueStr = value.toString();
xmlNodeSetContentLen(nodep,
(const xmlChar*)valueStr.data(),
valueStr.size() + 1);
}
break;
default:
break;
}
}
static Variant domnode_nodetype_read(const Object& obj) {
CHECK_NODE(nodep);
Variant retval;
/* Specs dictate that they are both type XML_DOCUMENT_TYPE_NODE */
if (nodep->type == XML_DTD_NODE) {
retval = XML_DOCUMENT_TYPE_NODE;
} else {
retval = nodep->type;
}
return retval;
}
static Variant domnode_parentnode_read(const Object& obj) {
CHECK_NODE(nodep);
return create_node_object(nodep->parent, domnode->doc());
}
static Variant domnode_childnodes_read(const Object& obj) {
CHECK_NODE(nodep);
if (!dom_node_children_valid(nodep)) {
return init_null();
}
return newDOMNodeList(domnode->doc(), obj, XML_ELEMENT_NODE);
}
static Variant domnode_firstchild_read(const Object& obj) {
CHECK_NODE(nodep);
xmlNode *first = nullptr;
if (dom_node_children_valid(nodep)) {
first = nodep->children;
}
return create_node_object(first, domnode->doc());
}
static Variant domnode_lastchild_read(const Object& obj) {
CHECK_NODE(nodep);
xmlNode *last = nullptr;
if (dom_node_children_valid(nodep)) {
last = nodep->last;
}
return create_node_object(last, domnode->doc());
}
static Variant domnode_previoussibling_read(const Object& obj) {
CHECK_NODE(nodep);
return create_node_object(nodep->prev, domnode->doc());
}
static Variant domnode_nextsibling_read(const Object& obj) {
CHECK_NODE(nodep);
return create_node_object(nodep->next, domnode->doc());
}
static Variant domnode_attributes_read(const Object& obj) {
CHECK_NODE(nodep);
if (nodep->type == XML_ELEMENT_NODE) {
return newDOMNamedNodeMap(domnode->doc(), obj, XML_ATTRIBUTE_NODE);
}
return init_null();
}
static Variant domnode_ownerdocument_read(const Object& obj) {
CHECK_NODE(nodep);
if (nodep->type == XML_DOCUMENT_NODE ||
nodep->type == XML_HTML_DOCUMENT_NODE) {
return init_null();
}
auto doc = domnode->doc();
if (doc && (nodep->doc == doc->docp())) {
return create_node_object((xmlNodePtr) doc->docp(), doc);
} else {
// The node wasn't created by this extension, so doesn't already have
// a DOMDocument - make one. dom_import_xml() is one way for this to
// happen.
return create_node_object((xmlNodePtr) nodep->doc, doc);
}
}
static Variant domnode_namespaceuri_read(const Object& obj) {
CHECK_NODE(nodep);
const char *str = nullptr;
switch (nodep->type) {
case XML_ELEMENT_NODE:
case XML_ATTRIBUTE_NODE:
case XML_NAMESPACE_DECL:
if (nodep->ns != nullptr) {
str = (const char*)nodep->ns->href;
}
break;
default:
break;
}
if (str) {
return String(str, CopyString);
}
return init_null();
}
static Variant domnode_prefix_read(const Object& obj) {
CHECK_NODE(nodep);
xmlNsPtr ns;
char *str = nullptr;
switch (nodep->type) {
case XML_ELEMENT_NODE:
case XML_ATTRIBUTE_NODE:
case XML_NAMESPACE_DECL:
ns = nodep->ns;
if (ns != nullptr && ns->prefix) {
str = (char *)ns->prefix;
}
break;
default:
break;
}
if (str) {
return String(str, CopyString);
}
return empty_string_variant();
}
static void domnode_prefix_write(const Object& obj, const Variant& value) {
String svalue;
xmlNode *nsnode = nullptr;
xmlNsPtr ns = nullptr, curns;
const char *strURI;
const char *prefix;
CHECK_WRITE_NODE(nodep);
switch (nodep->type) {
case XML_ELEMENT_NODE:
nsnode = nodep;
// fall through
case XML_ATTRIBUTE_NODE:
if (nsnode == nullptr) {
nsnode = nodep->parent;
if (nsnode == nullptr) {
nsnode = xmlDocGetRootElement(nodep->doc);
}
}
svalue = value.toString();
prefix = svalue.data();
if (nsnode && nodep->ns != nullptr &&
!xmlStrEqual(nodep->ns->prefix, (xmlChar *)prefix)) {
strURI = (char *) nodep->ns->href;
if (strURI == nullptr ||
(!strcmp (prefix, "xml") &&
strcmp(strURI, (const char *)XML_XML_NAMESPACE)) ||
(nodep->type == XML_ATTRIBUTE_NODE && !strcmp (prefix, "xmlns") &&
strcmp (strURI, (const char *)DOM_XMLNS_NAMESPACE)) ||
(nodep->type == XML_ATTRIBUTE_NODE &&
!strcmp ((const char*)nodep->name, "xmlns"))) {
ns = nullptr;
} else {
curns = nsnode->nsDef;
while (curns != nullptr) {
if (xmlStrEqual((xmlChar *)prefix, curns->prefix) &&
xmlStrEqual(nodep->ns->href, curns->href)) {
ns = curns;
break;
}
curns = curns->next;
}
if (ns == nullptr) {
ns = xmlNewNs(nsnode, nodep->ns->href, (xmlChar *)prefix);
}
}
if (ns == nullptr) {
php_dom_throw_error(NAMESPACE_ERR, domnode->doc()->m_stricterror);
return;
}
xmlSetNs(nodep, ns);
}
break;
default:
break;
}
}
static Variant domnode_localname_read(const Object& obj) {
CHECK_NODE(nodep);
if (nodep->type == XML_ELEMENT_NODE ||
nodep->type == XML_ATTRIBUTE_NODE ||
nodep->type == XML_NAMESPACE_DECL) {
return String((char *)(nodep->name), CopyString);
}
return init_null();
}
static Variant domnode_baseuri_read(const Object& obj) {
CHECK_NODE(nodep);
xmlChar *baseuri = xmlNodeGetBase(nodep->doc, nodep);
if (baseuri) {
String ret((char *)(baseuri), CopyString);
xmlFree(baseuri);
return ret;
}
return init_null();
}
static Variant domnode_textcontent_read(const Object& obj) {
CHECK_NODE(nodep);
char *str = (char *)xmlNodeGetContent(nodep);
if (str) {
String ret(str, CopyString);
xmlFree(str);
return ret;
}
return empty_string_variant();
}
static void domnode_textcontent_write(const Object& obj, const Variant& value) {
CHECK_WRITE_NODE(nodep);
if (nodep->type == XML_ELEMENT_NODE || nodep->type == XML_ATTRIBUTE_NODE) {
if (nodep->children) {
php_libxml_node_free_resource((xmlNodePtr) nodep->children);
nodep->children = nullptr;
}
}
xmlNodeSetContent(nodep, (xmlChar *) "");
xmlNodeAddContent(nodep, (xmlChar *) value.toString().data());
}
static DOMPropertyAccessor domnode_properties[] = {
{ "nodeName", domnode_nodename_read, nullptr },
{ "nodeValue", domnode_nodevalue_read, domnode_nodevalue_write },
{ "nodeType", domnode_nodetype_read, nullptr },
{ "parentNode", domnode_parentnode_read, nullptr },
{ "childNodes", domnode_childnodes_read, nullptr },
{ "firstChild", domnode_firstchild_read, nullptr },
{ "lastChild", domnode_lastchild_read, nullptr },
{ "previousSibling", domnode_previoussibling_read, nullptr },
{ "nextSibling", domnode_nextsibling_read, nullptr },
{ "attributes", domnode_attributes_read, nullptr },
{ "ownerDocument", domnode_ownerdocument_read, nullptr },
{ "namespaceURI", domnode_namespaceuri_read, nullptr },
{ "prefix", domnode_prefix_read, domnode_prefix_write },
{ "localName", domnode_localname_read, nullptr },
{ "baseURI", domnode_baseuri_read, nullptr },
{ "textContent", domnode_textcontent_read, domnode_textcontent_write },
{ nullptr, nullptr, nullptr}
};
static DOMPropertyAccessorMap domnode_properties_map
((DOMPropertyAccessor*)domnode_properties);
struct DOMNodePropHandler : public DOMPropHandler<DOMNodePropHandler> {
static constexpr DOMPropertyAccessorMap& map = domnode_properties_map;
};
///////////////////////////////////////////////////////////////////////////////
Array HHVM_METHOD(DOMNode, __debugInfo) {
auto* data = Native::data<DOMNode>(this_);
if (!data->node()) {
return this_->toArray();
}
return domnode_properties_map.debugInfo(Object{this_});
}
Variant HHVM_METHOD(DOMNode, appendChild,
const Object& newnode) {
auto* data = Native::data<DOMNode>(this_);
xmlNodePtr nodep = data->nodep();
if (!nodep) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return init_null();
}
if (!dom_node_children_valid(nodep)) {
return false;
}
auto* newdomnode = getDOMNode(newnode);
xmlNodePtr child = newdomnode->nodep();
if (!child) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return init_null();
}
xmlNodePtr new_child = nullptr;
bool stricterror = data->doc() ? data->doc()->m_stricterror : true;
if (dom_node_is_read_only(nodep) ||
(child->parent != nullptr && dom_node_is_read_only(child->parent))) {
php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
return false;
}
if (!dom_hierarchy(nodep, child)) {
php_dom_throw_error(HIERARCHY_REQUEST_ERR, data->doc()->m_stricterror);
return false;
}
if (!(child->doc == nullptr || child->doc == nodep->doc)) {
php_dom_throw_error(WRONG_DOCUMENT_ERR, data->doc()->m_stricterror);
return false;
}
if (child->type == XML_DOCUMENT_FRAG_NODE && child->children == nullptr) {
raise_warning("Document Fragment is empty");
return false;
}
if (child->parent != nullptr) {
xmlUnlinkNode(child);
}
if (child->type == XML_TEXT_NODE && nodep->last != nullptr &&
nodep->last->type == XML_TEXT_NODE) {
child->parent = nodep;
if (child->doc == nullptr) {
xmlSetTreeDoc(child, nodep->doc);
}
new_child = child;
if (nodep->children == nullptr) {
nodep->children = child;
nodep->last = child;
} else {
child = nodep->last;
child->next = new_child;
new_child->prev = child;
nodep->last = new_child;
}
} else if (child->type == XML_ATTRIBUTE_NODE) {
xmlAttrPtr lastattr;
if (child->ns == nullptr) {
lastattr = xmlHasProp(nodep, child->name);
} else {
lastattr = xmlHasNsProp(nodep, child->name, child->ns->href);
}
if (lastattr != nullptr && lastattr->type != XML_ATTRIBUTE_DECL) {
if (lastattr != (xmlAttrPtr)child) {
libxml_register_node((xmlNodePtr)lastattr)->unlink();
}
}
} else if (child->type == XML_DOCUMENT_FRAG_NODE) {
new_child = _php_dom_insert_fragment(nodep, nodep->last, nullptr, child);
}
if (new_child == nullptr) {
new_child = xmlAddChild(nodep, child);
if (new_child == nullptr) {
raise_warning("Couldn't append node");
return false;
}
}
dom_reconcile_ns(nodep->doc, new_child);
return create_node_object(new_child, data->doc());
}
DOMNode& DOMNode::operator=(const DOMNode& copy) {
if (auto copyNode = copy.nodep()) {
auto newNode = xmlDocCopyNode(copyNode, copyNode->doc, true /* deep */);
setNode(newNode);
if (auto d = copy.doc()) {
setDoc(std::move(d));
}
return *this;
}
if (m_node) {
assertx(m_node->getCache() &&
Native::data<DOMNode>(m_node->getCache()) == this);
m_node->clearCache();
m_node = nullptr;
}
return *this;
}
Variant HHVM_METHOD(DOMNode, cloneNode,
bool deep /* = false */) {
auto* data = Native::data<DOMNode>(this_);
xmlNodePtr n = data->nodep();
if (!n) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return init_null();
}
xmlNode* node = xmlDocCopyNode(n, n->doc, deep);
if (!node) {
return false;
}
// When deep is false Element nodes still require the attributes
// Following taken from libxml as xmlDocCopyNode doesnt do this
if (n->type == XML_ELEMENT_NODE && !deep) {
if (n->nsDef != nullptr) {
node->nsDef = xmlCopyNamespaceList(n->nsDef);
}
if (n->ns != nullptr) {
xmlNsPtr ns;
ns = xmlSearchNs(n->doc, node, n->ns->prefix);
if (ns == nullptr) {
ns = xmlSearchNs(n->doc, n, n->ns->prefix);
if (ns != nullptr) {
xmlNodePtr root = node;
while (root->parent != nullptr) {
root = root->parent;
}
node->ns = xmlNewNs(root, ns->href, ns->prefix);
}
} else {
node->ns = ns;
}
}
if (n->properties != nullptr) {
node->properties = xmlCopyPropList(node, n->properties);
}
}
return create_node_object(node, data->doc());
}
int64_t HHVM_METHOD(DOMNode, getLineNo) {
auto* data = Native::data<DOMNode>(this_);
xmlNodePtr nodep = data->nodep();
if (!nodep) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return 0;
}
return xmlGetLineNo(nodep);
}
bool HHVM_METHOD(DOMNode, hasAttributes) {
auto* data = Native::data<DOMNode>(this_);
xmlNodePtr nodep = data->nodep();
if (!nodep) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
if (nodep->type != XML_ELEMENT_NODE) {
return false;
}
return nodep->properties;
}
bool HHVM_METHOD(DOMNode, hasChildNodes) {
auto* data = Native::data<DOMNode>(this_);
xmlNodePtr nodep = data->nodep();
if (!nodep) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
if (!dom_node_children_valid(nodep)) {
return false;
}
return nodep->children;
}
Variant HHVM_METHOD(DOMNode, insertBefore,
const Object& newnode,
const Variant& refnode /* = null */) {
auto* data = Native::data<DOMNode>(this_);
xmlNodePtr parentp = data->nodep();
if (!parentp) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
if (!dom_node_children_valid(parentp)) {
return false;
}
auto* domchildnode = getDOMNode(newnode);
xmlNodePtr child = domchildnode->nodep();
if (!child) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
xmlNodePtr new_child = nullptr;
bool stricterror = data->doc() ? data->doc()->m_stricterror : true;
if (dom_node_is_read_only(parentp) ||
(child->parent != nullptr && dom_node_is_read_only(child->parent))) {
php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
return false;
}
if (!dom_hierarchy(parentp, child)) {
php_dom_throw_error(HIERARCHY_REQUEST_ERR, stricterror);
return false;
}
if (child->doc != parentp->doc && child->doc != nullptr) {
php_dom_throw_error(WRONG_DOCUMENT_ERR, stricterror);
return false;
}
if (child->type == XML_DOCUMENT_FRAG_NODE && child->children == nullptr) {
raise_warning("Document Fragment is empty");
return false;
}
if (!refnode.isNull()) {
auto* domrefnode = getDOMNode(refnode.toObject());
xmlNodePtr refp = domrefnode->nodep();
if (!refp || refp->parent != parentp) {
php_dom_throw_error(NOT_FOUND_ERR, stricterror);
return false;
}
if (child->parent != nullptr) {
xmlUnlinkNode(child);
}
if (child->type == XML_TEXT_NODE && (refp->type == XML_TEXT_NODE ||
(refp->prev != nullptr && refp->prev->type == XML_TEXT_NODE))) {
if (child->doc == nullptr) {
xmlSetTreeDoc(child, parentp->doc);
}
new_child = child;
new_child->parent = refp->parent;
new_child->next = refp;
new_child->prev = refp->prev;
refp->prev = new_child;
if (new_child->prev != nullptr) {
new_child->prev->next = new_child;
}
if (new_child->parent != nullptr) {
if (new_child->parent->children == refp) {
new_child->parent->children = new_child;
}
}
} else if (child->type == XML_ATTRIBUTE_NODE) {
xmlAttrPtr lastattr;
if (child->ns == nullptr) {
lastattr = xmlHasProp(refp->parent, child->name);
} else {
lastattr = xmlHasNsProp(refp->parent, child->name, child->ns->href);
}
if (lastattr != nullptr && lastattr->type != XML_ATTRIBUTE_DECL) {
if (lastattr != (xmlAttrPtr)child) {
libxml_register_node((xmlNodePtr)lastattr)->unlink();
} else {
return create_node_object(child, data->doc());
}
}
} else if (child->type == XML_DOCUMENT_FRAG_NODE) {
new_child = _php_dom_insert_fragment(parentp, refp->prev, refp, child);
}
if (new_child == nullptr) {
new_child = xmlAddPrevSibling(refp, child);
}
} else {
if (child->parent != nullptr) {
xmlUnlinkNode(child);
}
if (child->type == XML_TEXT_NODE && parentp->last != nullptr &&
parentp->last->type == XML_TEXT_NODE) {
child->parent = parentp;
if (child->doc == nullptr) {
xmlSetTreeDoc(child, parentp->doc);
}
new_child = child;
if (parentp->children == nullptr) {
parentp->children = child;
parentp->last = child;
} else {
child = parentp->last;
child->next = new_child;
new_child->prev = child;
parentp->last = new_child;
}
} else if (child->type == XML_ATTRIBUTE_NODE) {
xmlAttrPtr lastattr;
if (child->ns == nullptr)
lastattr = xmlHasProp(parentp, child->name);
else
lastattr = xmlHasNsProp(parentp, child->name, child->ns->href);
if (lastattr != nullptr && lastattr->type != XML_ATTRIBUTE_DECL) {
if (lastattr != (xmlAttrPtr)child) {
libxml_register_node((xmlNodePtr)lastattr)->unlink();
} else {
return create_node_object(child, data->doc());
}
}
} else if (child->type == XML_DOCUMENT_FRAG_NODE) {
new_child = _php_dom_insert_fragment(parentp, parentp->last, nullptr,
child);
}
if (new_child == nullptr) {
new_child = xmlAddChild(parentp, child);
}
}
if (nullptr == new_child) {
raise_warning("Couldn't add newnode as the previous sibling of refnode");
return false;
}
dom_reconcile_ns(parentp->doc, new_child);
return create_node_object(new_child, data->doc());
}
bool HHVM_METHOD(DOMNode, isDefaultNamespace,
const String& namespaceuri) {
auto* data = Native::data<DOMNode>(this_);
xmlNodePtr nodep = data->nodep();
if (!nodep) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
xmlNsPtr nsptr;
if (nodep->type == XML_DOCUMENT_NODE ||
nodep->type == XML_HTML_DOCUMENT_NODE) {
nodep = xmlDocGetRootElement((xmlDocPtr)nodep);
}
if (nodep && namespaceuri.size() > 0) {
nsptr = xmlSearchNs(nodep->doc, nodep, nullptr);
if (nsptr && xmlStrEqual(nsptr->href, (xmlChar*)namespaceuri.data())) {
return true;
}
}
return false;
}
bool HHVM_METHOD(DOMNode, isSameNode,
const Object& node) {
auto* data = Native::data<DOMNode>(this_);
auto* other_data = getDOMNode(node);
return data->nodep() == other_data->nodep();
}
bool HHVM_METHOD(DOMNode, isSupported,
const String& feature,
const String& version) {
return dom_has_feature(feature.data(), version.data());
}
Variant HHVM_METHOD(DOMNode, lookupNamespaceUri,
const Variant& namespaceuri) {
if (!namespaceuri.isString() && !namespaceuri.isNull()) {
raise_param_type_warning("DOMNode::lookupNamespaceUri", 1,
"string", *namespaceuri.asTypedValue());
return init_null();
}
auto* data = Native::data<DOMNode>(this_);
xmlNodePtr nodep = data->nodep();
if (!nodep) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return init_null();
}
xmlNsPtr nsptr;
if (nodep->type == XML_DOCUMENT_NODE ||
nodep->type == XML_HTML_DOCUMENT_NODE) {
nodep = xmlDocGetRootElement((xmlDocPtr) nodep);
if (nodep == nullptr) {
return init_null();
}
}
String nsuri = namespaceuri.toString();
const char* ns = nsuri.data();
if (namespaceuri.isNull()) {
ns = nullptr;
}
nsptr = xmlSearchNs(nodep->doc, nodep, (xmlChar*)ns);
if (nsptr && nsptr->href != nullptr) {
return String((char *)nsptr->href, CopyString);
}
return init_null();
}
Variant HHVM_METHOD(DOMNode, lookupPrefix,
const String& prefix) {
auto* data = Native::data<DOMNode>(this_);
xmlNodePtr nodep = data->nodep();
if (!nodep) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return init_null();
}
xmlNodePtr lookupp;
xmlNsPtr nsptr;
if (prefix.size() > 0) {
switch (nodep->type) {
case XML_ELEMENT_NODE:
lookupp = nodep;
break;
case XML_DOCUMENT_NODE:
case XML_HTML_DOCUMENT_NODE:
lookupp = xmlDocGetRootElement((xmlDocPtr)nodep);
break;
case XML_ENTITY_NODE:
case XML_NOTATION_NODE:
case XML_DOCUMENT_FRAG_NODE:
case XML_DOCUMENT_TYPE_NODE:
case XML_DTD_NODE:
return init_null();
default:
lookupp = nodep->parent;
}
if (lookupp != nullptr &&
(nsptr = xmlSearchNsByHref(lookupp->doc, lookupp,
(xmlChar*)prefix.data()))) {
if (nsptr->prefix != nullptr) {
return String((char *)nsptr->prefix, CopyString);
}
}
}
return init_null();
}
void HHVM_METHOD(DOMNode, normalize) {
auto* data = Native::data<DOMNode>(this_);
if (!data->nodep()) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return;
}
dom_normalize(data->nodep());
}
Variant HHVM_METHOD(DOMNode, removeChild,
const Object& node) {
auto* data = Native::data<DOMNode>(this_);
xmlNodePtr children;
xmlNodePtr nodep = data->nodep();
if (!nodep) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
if (!dom_node_children_valid(nodep)) {
return false;
}
auto* domnode2 = getDOMNode(node);
xmlNodePtr child = domnode2->nodep();
if (!child) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
bool stricterror = data->doc() ? data->doc()->m_stricterror : true;
if (dom_node_is_read_only(nodep) ||
(child->parent != nullptr && dom_node_is_read_only(child->parent))) {
php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
return false;
}
children = nodep->children;
if (!children) {
php_dom_throw_error(NOT_FOUND_ERR, stricterror);
return false;
}
while (children) {
if (children == child) {
xmlUnlinkNode(child);
return create_node_object(child, data->doc());
}
children = children->next;
}
php_dom_throw_error(NOT_FOUND_ERR, stricterror);
return false;
}
Variant HHVM_METHOD(DOMNode, replaceChild,
const Object& newchildobj,
const Object& oldchildobj) {
auto* data = Native::data<DOMNode>(this_);
int foundoldchild = 0;
xmlNodePtr nodep = data->nodep();
if (!nodep) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
if (!dom_node_children_valid(nodep)) {
return false;
}
auto* domnewchildnode = getDOMNode(newchildobj);
xmlNodePtr newchild = domnewchildnode->nodep();
if (!newchild) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
auto* domoldchildnode = getDOMNode(oldchildobj);
xmlNodePtr oldchild = domoldchildnode->nodep();
if (!oldchild) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
xmlNodePtr children = nodep->children;
if (!children) {
return false;
}
bool stricterror = data->doc() ? data->doc()->m_stricterror : true;
if (dom_node_is_read_only(nodep) ||
(newchild->parent != nullptr && dom_node_is_read_only(newchild->parent))) {
php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
return false;
}
if (newchild->doc != nodep->doc && newchild->doc != nullptr) {
php_dom_throw_error(WRONG_DOCUMENT_ERR, stricterror);
return false;
}
if (!dom_hierarchy(nodep, newchild)) {
php_dom_throw_error(HIERARCHY_REQUEST_ERR, stricterror);
return false;
}
// check for the old child and whether the new child is already a child
while (children) {
if (children == oldchild) {
foundoldchild = 1;
break;
}
children = children->next;
}
if (foundoldchild) {
if (newchild->type == XML_DOCUMENT_FRAG_NODE) {
xmlNodePtr prevsib, nextsib;
prevsib = oldchild->prev;
nextsib = oldchild->next;
xmlUnlinkNode(oldchild);
newchild = _php_dom_insert_fragment(nodep, prevsib, nextsib, newchild);
if (newchild) {
dom_reconcile_ns(nodep->doc, newchild);
}
} else if (oldchild != newchild) {
if (newchild->doc == nullptr && nodep->doc != nullptr) {
xmlSetTreeDoc(newchild, nodep->doc);
}
xmlReplaceNode(oldchild, newchild);
dom_reconcile_ns(nodep->doc, newchild);
}
return create_node_object(oldchild, data->doc());
}
php_dom_throw_error(NOT_FOUND_ERR, data->doc()->m_stricterror);
return false;
}
Variant HHVM_METHOD(DOMNode, C14N,
bool exclusive /* = false */,
bool with_comments /* = false */,
const Variant& xpath /* = null */,
const Variant& ns_prefixes /* = null */) {
auto* data = Native::data<DOMNode>(this_);
if (!data->nodep()) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
return dom_canonicalization(data->nodep(), "", exclusive, with_comments,
xpath, ns_prefixes, 0);
}
Variant HHVM_METHOD(DOMNode, C14Nfile,
const String& uri,
bool exclusive /* = false */,
bool with_comments /* = false */,
const Variant& xpath /* = null */,
const Variant& ns_prefixes /* = null */) {
auto* data = Native::data<DOMNode>(this_);
if (!data->nodep()) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
return dom_canonicalization(data->nodep(), uri, exclusive,
with_comments, xpath, ns_prefixes, 1);
}
Variant HHVM_METHOD(DOMNode, getNodePath) {
auto* data = Native::data<DOMNode>(this_);
xmlNodePtr n = data->nodep();
if (!n) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return init_null();
}
char *value = (char*)xmlGetNodePath(n);
if (value) {
String ret(value, CopyString);
xmlFree(value);
return ret;
}
return init_null();
}
///////////////////////////////////////////////////////////////////////////////
#define CHECK_ATTR(attrp) \
auto domattr = getDOMNode(obj); \
xmlAttrPtr attrp = (xmlAttrPtr)(domattr ? domattr->nodep() : nullptr); \
if (attrp == nullptr) { \
php_dom_throw_error(INVALID_STATE_ERR, 0); \
return init_null(); \
} \
/**/
#define CHECK_WRITE_ATTR(attrp) \
auto domattr = getDOMNode(obj); \
xmlAttrPtr attrp = (xmlAttrPtr)(domattr ? domattr->nodep() : nullptr); \
if (attrp == nullptr) { \
php_dom_throw_error(INVALID_STATE_ERR, 0); \
return; \
} \
/**/
static Variant domattr_name_read(const Object& obj) {
CHECK_ATTR(attrp);
return String((char *)(attrp->name), CopyString);
}
static Variant domattr_specified_read(const Object& /*obj*/) {
/* T O D O */
return true;
}
static Variant domattr_value_read(const Object& obj) {
CHECK_ATTR(attrp);
xmlChar *content = xmlNodeGetContent((xmlNodePtr) attrp);
if (content) {
String ret((const char*)content, CopyString);
xmlFree(content);
return ret;
}
return empty_string_variant();
}
static void domattr_value_write(const Object& obj, const Variant& value) {
CHECK_WRITE_ATTR(attrp);
String svalue = value.toString();
xmlNodeSetContentLen((xmlNodePtr)attrp, (xmlChar*)svalue.data(),
svalue.size() + 1);
}
static Variant domattr_ownerelement_read(const Object& obj) {
CHECK_NODE(nodep);
return create_node_object(nodep->parent, domnode->doc());
}
static Variant domattr_schematypeinfo_read(const Object& /*obj*/) {
raise_warning("Not yet implemented");
return init_null();
}
static DOMPropertyAccessor domattr_properties[] = {
{ "name", domattr_name_read, nullptr },
{ "specified", domattr_specified_read, nullptr },
{ "value", domattr_value_read, domattr_value_write },
{ "ownerElement", domattr_ownerelement_read, nullptr },
{ "schemaTypeInfo", domattr_schematypeinfo_read, nullptr },
{ nullptr, nullptr, nullptr}
};
static DOMPropertyAccessorMap domattr_properties_map
((DOMPropertyAccessor*)domattr_properties, &domnode_properties_map);
struct DOMAttrPropHandler : public DOMPropHandler<DOMAttrPropHandler> {
static constexpr DOMPropertyAccessorMap& map = domattr_properties_map;
};
///////////////////////////////////////////////////////////////////////////////
void HHVM_METHOD(DOMAttr, __construct,
const String& name,
const Variant& value /* = null_string */) {
auto data = Native::data<DOMNode>(this_);
int name_valid = xmlValidateName((xmlChar *)name.data(), 0);
if (name_valid != 0) {
php_dom_throw_error(INVALID_CHARACTER_ERR, 1);
return;
}
const String& str_value = value.isNull() ? null_string : value.toString();
data->setNode((xmlNodePtr)xmlNewProp(nullptr, (xmlChar*)name.data(),
(xmlChar*)str_value.data()));
if (!data->node()) {
php_dom_throw_error(INVALID_STATE_ERR, 1);
}
}
Array HHVM_METHOD(DOMAttr, __debugInfo) {
auto data = Native::data<DOMNode>(this_);
if (!data->node()) {
return this_->toArray();
}
return domattr_properties_map.debugInfo(Object{this_});
}
bool HHVM_METHOD(DOMAttr, isId) {
auto data = Native::data<DOMNode>(this_);
xmlAttrPtr attrp = (xmlAttrPtr)data->nodep();
if (!attrp) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
return attrp->atype == XML_ATTRIBUTE_ID;
}
///////////////////////////////////////////////////////////////////////////////
static Variant dom_characterdata_data_read(const Object& obj) {
CHECK_NODE(nodep);
xmlChar *content = xmlNodeGetContent(nodep);
if (content) {
String ret((const char*)content, CopyString);
xmlFree(content);
return ret;
}
return empty_string_variant();
}
static void dom_characterdata_data_write(const Object& obj, const Variant& value) {
CHECK_WRITE_NODE(nodep);
String svalue = value.toString();
xmlNodeSetContentLen(nodep, (xmlChar*)svalue.data(), svalue.size() + 1);
}
static Variant dom_characterdata_length_read(const Object& obj) {
CHECK_NODE(nodep);
int64_t length = 0;
xmlChar *content = xmlNodeGetContent(nodep);
if (content) {
length = xmlUTF8Strlen(content);
xmlFree(content);
}
return length;
}
static DOMPropertyAccessor domcharacterdata_properties[] = {
{ "data", dom_characterdata_data_read, dom_characterdata_data_write },
{ "length", dom_characterdata_length_read, nullptr },
{ nullptr, nullptr, nullptr}
};
static DOMPropertyAccessorMap domcharacterdata_properties_map
((DOMPropertyAccessor*)domcharacterdata_properties, &domnode_properties_map);
struct DOMCharacterDataPropHandler :
public DOMPropHandler<DOMCharacterDataPropHandler> {
static constexpr DOMPropertyAccessorMap& map =
domcharacterdata_properties_map;
};
///////////////////////////////////////////////////////////////////////////////
Array HHVM_METHOD(DOMCharacterData, __debugInfo) {
auto data = Native::data<DOMNode>(this_);
if (!data->node()) {
return this_->toArray();
}
return domcharacterdata_properties_map.debugInfo(Object{this_});
}
bool HHVM_METHOD(DOMCharacterData, appendData,
const String& arg) {
auto data = Native::data<DOMNode>(this_);
xmlNodePtr nodep = data->nodep();
if (!nodep) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
// Implement logic from libxml xmlTextConcat to add support for
// comments and PI
if ((nodep->content == (xmlChar *) &(nodep->properties)) ||
((nodep->doc != nullptr) && (nodep->doc->dict != nullptr) &&
xmlDictOwns(nodep->doc->dict, nodep->content))) {
nodep->content =
xmlStrncatNew(nodep->content, (xmlChar*)arg.data(), arg.size());
} else {
nodep->content =
xmlStrncat(nodep->content, (xmlChar*)arg.data(), arg.size());
}
nodep->properties = nullptr;
return true;
}
bool HHVM_METHOD(DOMCharacterData, deleteData,
int64_t offset,
int64_t count) {
auto data = Native::data<DOMNode>(this_);
xmlNodePtr node = data->nodep();
if (!node) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
xmlChar *cur, *substring, *second;
cur = xmlNodeGetContent(node);
if (cur == nullptr) {
return false;
}
int length = xmlUTF8Strlen(cur);
if (offset < 0 || count < 0 || offset > length) {
xmlFree(cur);
php_dom_throw_error(INDEX_SIZE_ERR, data->doc()->m_stricterror);
return false;
}
if (offset > 0) {
substring = xmlUTF8Strsub(cur, 0, offset);
} else {
substring = nullptr;
}
if ((offset + count) > length) {
count = length - offset;
}
second = xmlUTF8Strsub(cur, offset + count, length - offset);
substring = xmlStrcat(substring, second);
xmlNodeSetContent(node, substring);
xmlFree(cur);
xmlFree(second);
xmlFree(substring);
return true;
}
bool HHVM_METHOD(DOMCharacterData, insertData,
int64_t offset,
const String& data) {
auto native_data = Native::data<DOMNode>(this_);
xmlNodePtr node = native_data->nodep();
if (!node) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
xmlChar *cur, *first, *second;
cur = xmlNodeGetContent(node);
if (cur == nullptr) {
return false;
}
int length = xmlUTF8Strlen(cur);
if (offset < 0 || offset > length) {
xmlFree(cur);
php_dom_throw_error(INDEX_SIZE_ERR, native_data->doc()->m_stricterror);
return false;
}
first = xmlUTF8Strndup(cur, offset);
second = xmlUTF8Strsub(cur, offset, length - offset);
xmlFree(cur);
xmlNodeSetContent(node, first);
xmlNodeAddContent(node, (xmlChar*)data.data());
xmlNodeAddContent(node, second);
xmlFree(first);
xmlFree(second);
return true;
}
bool HHVM_METHOD(DOMCharacterData, replaceData,
int64_t offset,
int64_t count,
const String& data) {
auto native_data = Native::data<DOMNode>(this_);
xmlNodePtr node = native_data->nodep();
if (!node) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
xmlChar *cur, *substring, *second = nullptr;
cur = xmlNodeGetContent(node);
if (cur == nullptr) {
return false;
}
int length = xmlUTF8Strlen(cur);
if (offset < 0 || count < 0 || offset > length) {
xmlFree(cur);
php_dom_throw_error(INDEX_SIZE_ERR, native_data->doc()->m_stricterror);
return false;
}
if (offset > 0) {
substring = xmlUTF8Strsub(cur, 0, offset);
} else {
substring = nullptr;
}
if ((offset + count) > length) {
count = length - offset;
}
if (offset < length) {
second = xmlUTF8Strsub(cur, offset + count, length - offset);
}
substring = xmlStrcat(substring, (xmlChar*)data.data());
substring = xmlStrcat(substring, second);
xmlNodeSetContent(node, substring);
xmlFree(cur);
if (second) {
xmlFree(second);
}
xmlFree(substring);
return true;
}
String HHVM_METHOD(DOMCharacterData, substringData,
int64_t offset,
int64_t count) {
auto native_data = Native::data<DOMNode>(this_);
xmlNodePtr node = native_data->nodep();
if (!node) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
xmlChar *cur, *substring;
cur = xmlNodeGetContent(node);
if (cur == nullptr) {
return false;
}
int length = xmlUTF8Strlen(cur);
if (offset < 0 || count < 0 || offset > length) {
xmlFree(cur);
php_dom_throw_error(INDEX_SIZE_ERR, native_data->doc()->m_stricterror);
return false;
}
if ((offset + count) > length) {
count = length - offset;
}
substring = xmlUTF8Strsub(cur, offset, count);
xmlFree(cur);
if (substring) {
String ret((char*)substring, CopyString);
xmlFree(substring);
return ret;
}
return empty_string();
}
///////////////////////////////////////////////////////////////////////////////
void HHVM_METHOD(DOMComment, __construct,
const Variant& value /* = null_string */) {
auto data = Native::data<DOMNode>(this_);
const String& str_value = value.isNull() ? null_string : value.toString();
data->setNode(xmlNewComment((xmlChar *)str_value.data()));
if (!data->node()) {
php_dom_throw_error(INVALID_STATE_ERR, 1);
}
}
///////////////////////////////////////////////////////////////////////////////
static Variant dom_text_whole_text_read(const Object& obj) {
CHECK_NODE(node);
/* Find starting text node */
while (node->prev && ((node->prev->type == XML_TEXT_NODE) ||
(node->prev->type == XML_CDATA_SECTION_NODE))) {
node = node->prev;
}
/* concatenate all adjacent text and cdata nodes */
xmlChar *wholetext = nullptr;
while (node && ((node->type == XML_TEXT_NODE) ||
(node->type == XML_CDATA_SECTION_NODE))) {
wholetext = xmlStrcat(wholetext, node->content);
node = node->next;
}
if (wholetext) {
String ret((const char*)wholetext, CopyString);
xmlFree(wholetext);
return ret;
}
return empty_string_variant();
}
static DOMPropertyAccessor domtext_properties[] = {
{ "wholeText", dom_text_whole_text_read, nullptr },
{ nullptr, nullptr, nullptr}
};
static DOMPropertyAccessorMap domtext_properties_map
((DOMPropertyAccessor*)domtext_properties, &domcharacterdata_properties_map);
struct DOMTextPropHandler :
public DOMPropHandler<DOMTextPropHandler> {
static constexpr DOMPropertyAccessorMap& map = domtext_properties_map;
};
///////////////////////////////////////////////////////////////////////////////
void HHVM_METHOD(DOMText, __construct,
const Variant& value /* = null_string */) {
auto data = Native::data<DOMNode>(this_);
const String& str_value = value.isNull() ? null_string : value.toString();
data->setNode(xmlNewText((xmlChar *)str_value.data()));
if (!data->node()) {
php_dom_throw_error(INVALID_STATE_ERR, 1);
}
}
Array HHVM_METHOD(DOMText, __debugInfo) {
auto data = Native::data<DOMNode>(this_);
if (!data->node()) {
return this_->toArray();
}
return domtext_properties_map.debugInfo(Object{this_});
}
bool HHVM_METHOD(DOMText, isWhitespaceInElementContent) {
auto data = Native::data<DOMNode>(this_);
if (!data->nodep()) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
return xmlIsBlankNode(data->nodep());
}
bool HHVM_METHOD(DOMText, isElementContentWhitespace) {
auto data = Native::data<DOMNode>(this_);
if (!data->nodep()) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
return xmlIsBlankNode(data->nodep());
}
Variant HHVM_METHOD(DOMText, splitText,
int64_t offset) {
auto data = Native::data<DOMNode>(this_);
xmlNodePtr node = data->nodep();
if (!node) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
xmlChar *cur, *first, *second;
xmlNodePtr nnode;
if (node->type != XML_TEXT_NODE && node->type != XML_CDATA_SECTION_NODE) {
return false;
}
cur = xmlNodeGetContent(node);
if (cur == nullptr) {
return false;
}
int length = xmlUTF8Strlen(cur);
if (offset > length || offset < 0) {
xmlFree(cur);
return false;
}
first = xmlUTF8Strndup(cur, offset);
second = xmlUTF8Strsub(cur, offset, length - offset);
xmlFree(cur);
xmlNodeSetContent(node, first);
nnode = xmlNewDocText(node->doc, second);
xmlFree(first);
xmlFree(second);
if (nnode == nullptr) {
return false;
}
if (node->parent != nullptr) {
nnode->type = XML_ELEMENT_NODE;
xmlAddNextSibling(node, nnode);
nnode->type = XML_TEXT_NODE;
}
Object ret{getDOMTextClass()};
auto text_data = Native::data<DOMNode>(ret);
text_data->setNode(nnode);
text_data->setDoc(data->doc());
return ret;
}
///////////////////////////////////////////////////////////////////////////////
void HHVM_METHOD(DOMCdataSection, __construct,
const String& value) {
auto data = Native::data<DOMNode>(this_);
data->setNode(xmlNewCDataBlock(nullptr, (xmlChar *)value.data(),
value.size()));
if (!data->node()) {
php_dom_throw_error(INVALID_STATE_ERR, 1);
}
}
///////////////////////////////////////////////////////////////////////////////
#define CHECK_DOC(docp) \
auto domdoc = getDOMNode(obj); \
CHECK_THIS_DOC(docp, domdoc); \
#define CHECK_THIS_DOC(docp, domdoc) \
xmlDocPtr docp = (xmlDocPtr)(domdoc ? domdoc->nodep() : nullptr); \
if (docp == nullptr) { \
php_dom_throw_error(INVALID_STATE_ERR, 0); \
return init_null(); \
} \
#define CHECK_WRITE_DOC(docp) \
auto domdoc = getDOMNode(obj); \
CHECK_WRITE_THIS_DOC(docp, domdoc) \
#define CHECK_WRITE_THIS_DOC(docp, domdoc) \
xmlDocPtr docp = (xmlDocPtr)(domdoc ? domdoc->nodep() : nullptr); \
if (docp == nullptr) { \
php_dom_throw_error(INVALID_STATE_ERR, 0); \
return; \
} \
static Variant dom_document_doctype_read(const Object& obj) {
CHECK_DOC(docp);
auto const& dtd = (xmlNodePtr)xmlGetIntSubset(docp);
if (dtd == nullptr) {
return init_null();
}
return create_node_object(dtd, obj);
}
static Variant dom_document_implementation_read(const Object& /*obj*/) {
return Object{getDOMImplementationClass()};
}
static Variant dom_document_document_element_read(const Object& obj) {
CHECK_DOC(docp);
return create_node_object(xmlDocGetRootElement(docp), obj);
}
static Variant dom_document_encoding_read(const Object& obj) {
CHECK_DOC(docp);
char *encoding = (char *) docp->encoding;
if (encoding) {
return String(encoding, CopyString);
}
return init_null();
}
static void dom_document_encoding_write(const Object& obj,
const Variant& value) {
CHECK_WRITE_DOC(docp);
String svalue = value.toString();
xmlCharEncodingHandlerPtr handler =
xmlFindCharEncodingHandler(svalue.data());
if (handler) {
xmlCharEncCloseFunc(handler);
if (docp->encoding) {
xmlFree((xmlChar *)docp->encoding);
}
docp->encoding = xmlStrdup((const xmlChar *)svalue.data());
} else {
raise_warning("Invalid Document Encoding");
}
}
static Variant dom_document_standalone_read(const Object& obj) {
CHECK_DOC(docp);
return (bool)docp->standalone;
}
static void dom_document_standalone_write(const Object& obj,
const Variant& value) {
CHECK_WRITE_DOC(docp);
int64_t standalone = value.toInt64();
if (standalone > 0) {
docp->standalone = 1;
} else if (standalone < 0) {
docp->standalone = -1;
} else {
docp->standalone = 0;
}
}
static Variant dom_document_version_read(const Object& obj) {
CHECK_DOC(docp);
char *version = (char *) docp->version;
if (version) {
return String(version, CopyString);
}
return init_null();
}
static void dom_document_version_write(const Object& obj,
const Variant& value) {
CHECK_WRITE_DOC(docp);
if (docp->version != nullptr) {
xmlFree((xmlChar *)docp->version);
}
String svalue = value.toString();
docp->version = xmlStrdup((const xmlChar *)svalue.data());
}
#define DOCPROP_READ_WRITE(member, name) \
static Variant dom_document_ ##name## _read(const Object& obj) { \
CHECK_DOC(docp) \
return (bool)domdoc->doc()->m_ ## member; \
} \
static void dom_document_ ##name## _write(const Object& obj, \
const Variant& value) { \
CHECK_WRITE_DOC(docp) \
domdoc->doc()->m_ ## member = value.toBoolean(); \
} \
/**/
DOCPROP_READ_WRITE(stricterror, strict_error_checking );
DOCPROP_READ_WRITE(formatoutput, format_output );
DOCPROP_READ_WRITE(validateonparse, validate_on_parse );
DOCPROP_READ_WRITE(resolveexternals, resolve_externals );
DOCPROP_READ_WRITE(preservewhitespace, preserve_whitespace );
DOCPROP_READ_WRITE(recover, recover );
DOCPROP_READ_WRITE(substituteentities, substitue_entities );
static Variant dom_document_document_uri_read(const Object& obj) {
CHECK_DOC(docp);
char *url = (char *)docp->URL;
if (url) {
return String(url, CopyString);
}
return init_null();
}
static void dom_document_document_uri_write(const Object& obj,
const Variant& value) {
CHECK_WRITE_DOC(docp);
if (docp->URL != nullptr) {
xmlFree((xmlChar *) docp->URL);
}
String svalue = value.toString();
docp->URL = xmlStrdup((const xmlChar *)svalue.data());
}
static Variant dom_document_config_read(const Object& /*obj*/) {
return init_null();
}
/* }}} */
static DOMPropertyAccessor domdocument_properties[] = {
{ "doctype", dom_document_doctype_read, nullptr },
{ "implementation", dom_document_implementation_read, nullptr },
{ "documentElement", dom_document_document_element_read, nullptr },
{ "actualEncoding", dom_document_encoding_read, nullptr },
{ "encoding", dom_document_encoding_read,
dom_document_encoding_write },
{ "xmlEncoding", dom_document_encoding_read, nullptr },
{ "standalone", dom_document_standalone_read,
dom_document_standalone_write },
{ "xmlStandalone", dom_document_standalone_read,
dom_document_standalone_write },
{ "version", dom_document_version_read,
dom_document_version_write },
{ "xmlVersion", dom_document_version_read,
dom_document_version_write },
{ "strictErrorChecking", dom_document_strict_error_checking_read,
dom_document_strict_error_checking_write },
{ "documentURI", dom_document_document_uri_read,
dom_document_document_uri_write },
{ "config", dom_document_config_read, nullptr },
{ "formatOutput", dom_document_format_output_read,
dom_document_format_output_write },
{ "validateOnParse", dom_document_validate_on_parse_read,
dom_document_validate_on_parse_write },
{ "resolveExternals", dom_document_resolve_externals_read,
dom_document_resolve_externals_write },
{ "preserveWhiteSpace", dom_document_preserve_whitespace_read,
dom_document_preserve_whitespace_write },
{ "recover", dom_document_recover_read,
dom_document_recover_write },
{ "substituteEntities", dom_document_substitue_entities_read,
dom_document_substitue_entities_write },
{ nullptr, nullptr, nullptr}
};
static DOMPropertyAccessorMap domdocument_properties_map
((DOMPropertyAccessor*)domdocument_properties, &domnode_properties_map);
struct DOMDocumentPropHandler : public DOMPropHandler<DOMDocumentPropHandler> {
static constexpr DOMPropertyAccessorMap& map = domdocument_properties_map;
};
///////////////////////////////////////////////////////////////////////////////
void HHVM_METHOD(DOMDocument, __construct,
const Variant& version /* = null_string */,
const Variant& encoding /* = null_string */) {
xmlDoc *docp = xmlNewDoc(
version.isNull() ? nullptr : (xmlChar*)version.toString().data());
if (!docp) {
php_dom_throw_error(INVALID_STATE_ERR, 1);
return;
}
const String& str_encoding = encoding.isNull()
? null_string : encoding.toString();
if (str_encoding.size() > 0) {
docp->encoding = (const xmlChar*)xmlStrdup((xmlChar*)str_encoding.data());
}
auto* data = Native::data<DOMNode>(this_);
data->setNode((xmlNodePtr)docp);
}
Array HHVM_METHOD(DOMDocument, __debugInfo) {
auto* data = Native::data<DOMNode>(this_);
if (!data->node()) {
return this_->toArray();
}
return domdocument_properties_map.debugInfo(Object{this_});
}
Variant HHVM_METHOD(DOMDocument, createAttribute,
const String& name) {
auto* data = Native::data<DOMNode>(this_);
xmlDocPtr docp = (xmlDocPtr)data->nodep();
if (xmlValidateName((xmlChar*)name.data(), 0) != 0) {
php_dom_throw_error(INVALID_CHARACTER_ERR, data->doc()->m_stricterror);
return false;
}
xmlAttrPtr node = xmlNewDocProp(docp, (xmlChar*)name.data(), nullptr);
if (!node) {
return false;
}
node->doc = docp;
auto ret = php_dom_create_object((xmlNodePtr)node, data->doc());
if (ret.isNull()) return false;
return ret;
}
Variant HHVM_METHOD(DOMDocument, createAttributeNS,
const String& namespaceuri,
const String& qualifiedname) {
auto* data = Native::data<DOMNode>(this_);
xmlDocPtr docp = (xmlDocPtr)data->nodep();
xmlNodePtr nodep = nullptr;
xmlNsPtr nsptr;
char *localname = nullptr, *prefix = nullptr;
int errorcode;
xmlNodePtr root = xmlDocGetRootElement(docp);
if (root != nullptr) {
errorcode = dom_check_qname((char*)qualifiedname.data(), &localname,
&prefix, namespaceuri.size(),
qualifiedname.size());
if (errorcode == 0) {
if (xmlValidateName((xmlChar*)localname, 0) == 0) {
nodep = (xmlNodePtr)xmlNewDocProp(docp, (xmlChar*)localname, nullptr);
if (nodep != nullptr && namespaceuri.size() > 0) {
nsptr = xmlSearchNsByHref(nodep->doc, root,
(xmlChar*)namespaceuri.data());
if (nsptr == nullptr) {
nsptr = dom_get_ns(root, (char*)namespaceuri.data(),
&errorcode, prefix);
}
xmlSetNs(nodep, nsptr);
}
} else {
errorcode = INVALID_CHARACTER_ERR;
}
}
} else {
raise_warning("Document Missing Root Element");
return false;
}
xmlFree(localname);
if (prefix != nullptr) {
xmlFree(prefix);
}
if (errorcode != 0) {
if (nodep != nullptr) {
xmlFreeProp((xmlAttrPtr) nodep);
}
php_dom_throw_error((dom_exception_code)errorcode,
data->doc()->m_stricterror);
return false;
}
if (nodep == nullptr) {
return false;
}
nodep->doc = docp;
auto ret = php_dom_create_object(nodep, data->doc());
if (ret.isNull()) return false;
return ret;
}
Variant HHVM_METHOD(DOMDocument, createCDATASection,
const String& data) {
auto* native_data = Native::data<DOMNode>(this_);
xmlDocPtr docp = (xmlDocPtr)native_data->nodep();
xmlNode *node = xmlNewCDataBlock(docp, (xmlChar*)data.data(), data.size());
if (!node) {
return false;
}
node->doc = docp;
auto ret = php_dom_create_object(node, native_data->doc());
if (ret.isNull()) return false;
return ret;
}
Variant HHVM_METHOD(DOMDocument, createComment,
const String& data) {
auto* native_data = Native::data<DOMNode>(this_);
xmlDocPtr docp = (xmlDocPtr)native_data->nodep();
xmlNode *node = xmlNewComment((xmlChar*)data.data());
if (!node) {
return false;
}
node->doc = docp;
auto ret = php_dom_create_object(node, native_data->doc());
if (ret.isNull()) return false;
return ret;
}
Variant HHVM_METHOD(DOMDocument, createDocumentFragment) {
auto* data = Native::data<DOMNode>(this_);
xmlDocPtr docp = (xmlDocPtr)data->nodep();
xmlNode *node = xmlNewDocFragment(docp);
if (!node) {
return false;
}
node->doc = docp;
auto ret = php_dom_create_object(node, data->doc());
if (ret.isNull()) return false;
return ret;
}
Variant HHVM_METHOD(DOMDocument, createElement,
const String& name,
const Variant& value /*= null_string*/) {
auto* data = Native::data<DOMNode>(this_);
xmlDocPtr docp = (xmlDocPtr)data->nodep();
if (xmlValidateName((xmlChar*)name.data(), 0) != 0) {
php_dom_throw_error(INVALID_CHARACTER_ERR,
data->doc()->m_stricterror);
return false;
}
const String& str_value = value.isNull() ? null_string : value.toString();
xmlNode *node = xmlNewDocNode(docp, nullptr, (xmlChar*)name.data(),
(xmlChar*)getStringData(str_value));
if (!node) {
return false;
}
node->doc = docp;
auto ret = php_dom_create_object(node, data->doc());
if (ret.isNull()) return false;
return ret;
}
Variant HHVM_METHOD(DOMDocument, createElementNS,
const String& namespaceuri,
const String& qualifiedname,
const Variant& value /*= null_string*/) {
auto* data = Native::data<DOMNode>(this_);
xmlDocPtr docp = (xmlDocPtr)data->nodep();
xmlNodePtr nodep = nullptr;
xmlNsPtr nsptr = nullptr;
char *localname = nullptr, *prefix = nullptr;
int errorcode = dom_check_qname((char*)qualifiedname.data(), &localname,
&prefix, namespaceuri.size(),
qualifiedname.size());
if (errorcode == 0) {
if (xmlValidateName((xmlChar*)localname, 0) == 0) {
const String& str_value = value.isNull() ? null_string : value.toString();
nodep = xmlNewDocNode(docp, nullptr, (xmlChar*)localname,
(xmlChar*)getStringData(str_value));
if (nodep != nullptr && !namespaceuri.isNull()) {
nsptr = xmlSearchNsByHref(nodep->doc, nodep,
(xmlChar*)namespaceuri.data());
if (nsptr == nullptr) {
nsptr = dom_get_ns(nodep, (char*)namespaceuri.data(),
&errorcode, prefix);
}
xmlSetNs(nodep, nsptr);
}
} else {
errorcode = INVALID_CHARACTER_ERR;
}
}
xmlFree(localname);
if (prefix != nullptr) {
xmlFree(prefix);
}
if (errorcode != 0) {
if (nodep != nullptr) {
xmlFreeNode(nodep);
}
php_dom_throw_error((dom_exception_code)errorcode,
data->doc()->m_stricterror);
return false;
}
if (nodep == nullptr) {
return false;
}
nodep->doc = docp;
nodep->ns = nsptr;
auto ret = php_dom_create_object(nodep, data->doc());
if (ret.isNull()) return false;
return ret;
}
Variant HHVM_METHOD(DOMDocument, createEntityReference,
const String& name) {
auto* data = Native::data<DOMNode>(this_);
xmlDocPtr docp = (xmlDocPtr)data->nodep();
if (xmlValidateName((xmlChar*)name.data(), 0) != 0) {
php_dom_throw_error(INVALID_CHARACTER_ERR, data->doc()->m_stricterror);
return false;
}
xmlNode *node = xmlNewReference(docp, (xmlChar*)name.data());
if (!node) {
return false;
}
node->doc = docp;
auto ret = php_dom_create_object(node, data->doc());
if (ret.isNull()) return false;
return ret;
}
Variant HHVM_METHOD(DOMDocument, createProcessingInstruction,
const String& target,
const Variant& data /* = null_string */) {
auto* native_data = Native::data<DOMNode>(this_);
xmlDocPtr docp = (xmlDocPtr)native_data->nodep();
if (xmlValidateName((xmlChar*)target.data(), 0) != 0) {
php_dom_throw_error(INVALID_CHARACTER_ERR,
native_data->doc()->m_stricterror);
return false;
}
const String& str_data = data.isNull() ? null_string : data.toString();
xmlNode *node = xmlNewPI((xmlChar*)target.data(),
(xmlChar*)getStringData(str_data));
if (!node) {
return false;
}
node->doc = docp;
auto ret = php_dom_create_object(node, native_data->doc());
if (ret.isNull()) return false;
return ret;
}
Variant HHVM_METHOD(DOMDocument, createTextNode,
const String& data) {
auto* native_data = Native::data<DOMNode>(this_);
xmlDocPtr docp = (xmlDocPtr)native_data->nodep();
xmlNode *node = xmlNewDocText(docp, (xmlChar*)data.data());
if (!node) {
return false;
}
node->doc = docp;
auto ret = php_dom_create_object(node, native_data->doc());
if (ret.isNull()) return false;
return ret;
}
Variant HHVM_METHOD(DOMDocument, getElementById,
const String& elementid) {
auto* data = Native::data<DOMNode>(this_);
xmlDocPtr docp = (xmlDocPtr)data->nodep();
xmlAttrPtr attrp = xmlGetID(docp, (xmlChar*)elementid.data());
if (attrp && attrp->parent) {
return create_node_object(attrp->parent, Object{this_});
}
return init_null();
}
Variant HHVM_METHOD(DOMDocument, getElementsByTagName,
const String& name) {
auto* data = Native::data<DOMNode>(this_);
return newDOMNodeList(data->doc(), Object{this_}, 0, name);
}
Variant HHVM_METHOD(DOMDocument, getElementsByTagNameNS,
const String& namespaceuri,
const String& localname) {
auto* data = Native::data<DOMNode>(this_);
return newDOMNodeList(data->doc(), Object{this_}, 0, localname, namespaceuri);
}
Variant HHVM_METHOD(DOMDocument, importNode,
const Object& importednode,
bool deep /* = false */) {
auto* data = Native::data<DOMNode>(this_);
xmlDocPtr docp = (xmlDocPtr)data->nodep();
DOMNode *domnode = getDOMNode(importednode);
xmlNodePtr nodep = domnode->nodep();
xmlNodePtr retnodep;
long recursive = deep ? 1 : 0;
if (nodep->type == XML_HTML_DOCUMENT_NODE ||
nodep->type == XML_DOCUMENT_NODE ||
nodep->type == XML_DOCUMENT_TYPE_NODE) {
raise_warning("Cannot import: Node Type Not Supported");
return false;
}
if (nodep->doc == docp) {
retnodep = nodep;
} else {
if ((recursive == 0) && (nodep->type == XML_ELEMENT_NODE)) {
recursive = 2;
}
retnodep = xmlDocCopyNode(nodep, docp, recursive);
if (!retnodep) {
return false;
}
}
if ((retnodep->type == XML_ATTRIBUTE_NODE) && (nodep->ns != nullptr)) {
xmlNsPtr nsptr = nullptr;
xmlNodePtr root = xmlDocGetRootElement(docp);
nsptr = xmlSearchNsByHref (nodep->doc, root, nodep->ns->href);
if (nsptr == nullptr) {
int errorcode;
nsptr = dom_get_ns(root, (char *) nodep->ns->href, &errorcode, (char *) nodep->ns->prefix);
}
xmlSetNs(retnodep, nsptr);
}
return create_node_object(retnodep, data->doc());
}
void HHVM_METHOD(DOMDocument, normalizeDocument) {
auto* data = Native::data<DOMNode>(this_);
CHECK_WRITE_THIS_DOC(docp, data);
dom_normalize(data->nodep());
}
bool HHVM_METHOD(DOMDocument, registerNodeClass,
const String& baseclass,
const String& extendedclass) {
if (!HHVM_FN(class_exists)(baseclass)) {
raise_error("Class %s does not exist", baseclass.data());
return false;
}
if (!HHVM_FN(is_a)(baseclass, "DOMNode", true)) {
raise_error("Class %s is not DOMNode or derived from it.",
baseclass.data());
return false;
}
if (!HHVM_FN(class_exists)(extendedclass)) {
raise_error("Class %s does not exist", extendedclass.data());
return false;
}
if (!HHVM_FN(is_subclass_of)(extendedclass, baseclass)) {
raise_error("Class %s is not derived from %s.", extendedclass.data(),
baseclass.data());
return false;
}
auto* data = Native::data<DOMNode>(this_);
data->doc()->m_classmap.set(HHVM_FN(strtolower)(baseclass), extendedclass);
return true;
}
bool HHVM_METHOD(DOMDocument, relaxNGValidate,
const String& filename) {
VMRegGuard _;
auto* data = Native::data<DOMNode>(this_);
return _dom_document_relaxNG_validate(data, filename, DOM_LOAD_FILE);
}
bool HHVM_METHOD(DOMDocument, relaxNGValidateSource, const String& source) {
VMRegGuard _;
auto* data = Native::data<DOMNode>(this_);
return _dom_document_relaxNG_validate(data, source, DOM_LOAD_STRING);
}
Variant HHVM_METHOD(DOMDocument, save,
const String& file,
int64_t options /* = 0 */) {
VMRegGuard _;
auto* data = Native::data<DOMNode>(this_);
xmlDocPtr docp = (xmlDocPtr)data->nodep();
int bytes, format = 0, saveempty = 0;
/* encoding handled by property on doc */
format = data->doc()->m_formatoutput;
if (options & LIBXML_SAVE_NOEMPTYTAG) {
saveempty = xmlSaveNoEmptyTags;
xmlSaveNoEmptyTags = 1;
}
bytes = xmlSaveFormatFileEnc(file.data(), docp, nullptr, format);
if (options & LIBXML_SAVE_NOEMPTYTAG) {
xmlSaveNoEmptyTags = saveempty;
}
if (bytes == -1) {
return false;
}
return bytes;
}
Variant HHVM_METHOD(DOMDocument, saveHTMLFile,
const String& file) {
VMRegGuard _;
auto* data = Native::data<DOMNode>(this_);
xmlDocPtr docp = (xmlDocPtr)data->nodep();
int bytes, format = 0;
/* encoding handled by property on doc */
format = data->doc()->m_formatoutput;
bytes = htmlSaveFileFormat(file.data(), docp, nullptr, format);
if (bytes == -1) {
return false;
}
return bytes;
}
Variant HHVM_METHOD(DOMDocument, saveXML,
const Variant& node /* = null_object */,
int64_t options /* = 0 */) {
auto* data = Native::data<DOMNode>(this_);
int saveempty = 0;
if (options & LIBXML_SAVE_NOEMPTYTAG) {
saveempty = xmlSaveNoEmptyTags;
xmlSaveNoEmptyTags = 1;
}
const Object& obj_node = node.isNull() ? null_object : node.toObject();
Variant ret = save_html_or_xml(data, /* as_xml = */ true, obj_node);
if (options & LIBXML_SAVE_NOEMPTYTAG) {
xmlSaveNoEmptyTags = saveempty;
}
return ret;
}
Variant HHVM_METHOD(DOMDocument, saveHTML,
const Variant& node /* = uninit_variant */) {
auto* data = Native::data<DOMNode>(this_);
const Object& obj_node = node.isNull() ? null_object : node.toObject();
return save_html_or_xml(data, /* as_xml = */ false, obj_node);
}
Variant save_html_or_xml(DOMNode* obj,
bool as_xml,
const Object& node /* = null_object */) {
VMRegGuard _;
xmlDocPtr docp = (xmlDocPtr)obj->nodep();
xmlBufferPtr buf;
xmlChar *mem;
int size;
if (!node.isNull()) {
DOMNode* domnode = getDOMNode(node);
xmlNodePtr node = domnode->nodep();
if (!node) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
/* Dump contents of Node */
if (node->doc != docp) {
php_dom_throw_error(WRONG_DOCUMENT_ERR, obj->doc()->m_stricterror);
return false;
}
buf = xmlBufferCreate();
if (!buf) {
raise_warning("Could not fetch buffer");
return false;
}
if (as_xml) {
xmlNodeDump(buf, docp, node, 0, obj->doc()->m_formatoutput);
} else {
htmlNodeDump(buf, docp, node);
}
mem = (xmlChar*)xmlBufferContent(buf);
if (!mem) {
xmlBufferFree(buf);
return false;
}
String ret = String((char*)mem, CopyString);
xmlBufferFree(buf);
return ret;
}
if (as_xml) {
xmlDocDumpFormatMemory(docp, &mem, &size, obj->doc()->m_formatoutput);
} else {
#if LIBXML_VERSION >= 20623
htmlDocDumpMemoryFormat(docp, &mem, &size, obj->doc()->m_formatoutput);
#else
htmlDocDumpMemory(docp, &mem, &size);
#endif
}
if (!size) {
if (mem) xmlFree(mem);
return false;
}
String ret = String((char*)mem, size, CopyString);
xmlFree(mem);
return ret;
}
bool HHVM_METHOD(DOMDocument, schemaValidate,
const String& filename) {
VMRegGuard _;
auto* data = Native::data<DOMNode>(this_);
return _dom_document_schema_validate(data, filename, DOM_LOAD_FILE);
}
bool HHVM_METHOD(DOMDocument, schemaValidateSource,
const String& source) {
VMRegGuard _;
auto* data = Native::data<DOMNode>(this_);
return _dom_document_schema_validate(data, source, DOM_LOAD_STRING);
}
bool HHVM_METHOD(DOMDocument, validate) {
VMRegGuard _;
auto* data = Native::data<DOMNode>(this_);
xmlDocPtr docp = (xmlDocPtr)data->nodep();
xmlValidCtxt *cvp;
if (docp->intSubset == nullptr) {
raise_notice("No DTD given in XML-Document");
}
cvp = xmlNewValidCtxt();
cvp->userData = nullptr;
cvp->error = (xmlValidityErrorFunc) php_libxml_ctx_error;
cvp->warning = (xmlValidityErrorFunc) php_libxml_ctx_error;
bool ret = xmlValidateDocument(cvp, docp);
xmlFreeValidCtxt(cvp);
return ret;
}
Variant HHVM_METHOD(DOMDocument, xinclude,
int64_t options /* = 0 */) {
VMRegGuard _;
auto* data = Native::data<DOMNode>(this_);
xmlDocPtr docp = (xmlDocPtr)data->nodep();
int err = xmlXIncludeProcessFlags(docp, options);
/* XML_XINCLUDE_START and XML_XINCLUDE_END nodes need to be removed as these
are added via xmlXIncludeProcess to mark beginning and ending of
xincluded document, but are not wanted in resulting document - must be
done even if err as it could fail after having processed some xincludes */
xmlNodePtr root = (xmlNodePtr) docp->children;
while (root && root->type != XML_ELEMENT_NODE &&
root->type != XML_XINCLUDE_START) {
root = root->next;
}
if (root) {
php_dom_remove_xinclude_nodes(root);
}
if (err) {
return err;
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
void HHVM_METHOD(DOMDocumentFragment, __construct) {
auto data = Native::data<DOMNode>(this_);
data->setNode(xmlNewDocFragment(nullptr));
if (!data->node()) {
php_dom_throw_error(INVALID_STATE_ERR, 1);
}
}
bool HHVM_METHOD(DOMDocumentFragment, appendXML,
const String& data) {
auto native_data = Native::data<DOMNode>(this_);
xmlNodePtr nodep = native_data->nodep();
if (!nodep) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
bool stricterror = native_data->doc()
? native_data->doc()->m_stricterror
: true;
if (dom_node_is_read_only(nodep)) {
php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
return false;
}
if (!data.empty()) {
xmlNodePtr lst;
if (xmlParseBalancedChunkMemory(nodep->doc, nullptr, nullptr, 0,
(xmlChar*)data.data(), &lst)) {
return false;
}
/* Following needed due to bug in libxml2 <= 2.6.14
ifdef after next libxml release as bug is fixed in their cvs */
php_dom_xmlSetTreeDoc(lst, nodep->doc);
/* End stupid hack */
xmlAddChildList(nodep, lst);
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
#define CHECK_DOCTYPE(dtdptr) \
auto domdoctype = getDOMNode(obj); \
xmlDtdPtr dtdptr = (xmlDtdPtr)(domdoctype ? domdoctype->nodep() \
: nullptr); \
if (dtdptr == nullptr) { \
php_dom_throw_error(INVALID_STATE_ERR, 0); \
return init_null(); \
} \
static Variant dom_documenttype_name_read(const Object& obj) {
CHECK_DOCTYPE(dtdptr);
return String((char *)(dtdptr->name), CopyString);
}
static Variant dom_documenttype_entities_read(const Object& obj) {
CHECK_DOCTYPE(doctypep);
return newDOMNamedNodeMap(domdoctype->doc(), obj,
XML_ENTITY_NODE,
(xmlHashTable*) doctypep->entities);
}
static Variant dom_documenttype_notations_read(const Object& obj) {
CHECK_DOCTYPE(doctypep);
return newDOMNamedNodeMap(domdoctype->doc(), obj,
XML_NOTATION_NODE,
(xmlHashTable*) doctypep->notations);
}
static Variant dom_documenttype_public_id_read(const Object& obj) {
CHECK_DOCTYPE(dtdptr);
if (dtdptr->ExternalID) {
return String((char *)(dtdptr->ExternalID), CopyString);
}
return empty_string_variant();
}
static Variant dom_documenttype_system_id_read(const Object& obj) {
CHECK_DOCTYPE(dtdptr);
if (dtdptr->SystemID) {
return String((char *)(dtdptr->SystemID), CopyString);
}
return empty_string_variant();
}
static Variant dom_documenttype_internal_subset_read(const Object& obj) {
CHECK_DOCTYPE(dtdptr);
xmlDtd *intsubset;
xmlOutputBuffer *buff = nullptr;
xmlChar *strintsubset;
if (dtdptr->doc != nullptr &&
((intsubset = dtdptr->doc->intSubset) != nullptr)) {
buff = xmlAllocOutputBuffer(nullptr);
if (buff != nullptr) {
xmlNodeDumpOutput (buff, nullptr, (xmlNodePtr) intsubset, 0, 0, nullptr);
xmlOutputBufferFlush(buff);
strintsubset = xmlStrndup(xmlOutputBufferGetContent(buff),
xmlOutputBufferGetSize(buff));
(void)xmlOutputBufferClose(buff);
return String((char *)strintsubset, CopyString);
}
}
return empty_string_variant();
}
static DOMPropertyAccessor domdocumenttype_properties[] = {
{ "name", dom_documenttype_name_read, nullptr },
{ "entities", dom_documenttype_entities_read, nullptr },
{ "notations", dom_documenttype_notations_read, nullptr },
{ "publicId", dom_documenttype_public_id_read, nullptr },
{ "systemId", dom_documenttype_system_id_read, nullptr },
{ "internalSubset", dom_documenttype_internal_subset_read, nullptr },
{ nullptr, nullptr, nullptr}
};
static DOMPropertyAccessorMap domdocumenttype_properties_map
((DOMPropertyAccessor*)domdocumenttype_properties, &domnode_properties_map);
struct DOMDocumentTypePropHandler :
public DOMPropHandler<DOMDocumentTypePropHandler> {
static constexpr DOMPropertyAccessorMap& map = domdocumenttype_properties_map;
};
///////////////////////////////////////////////////////////////////////////////
Array HHVM_METHOD(DOMDocumentType, __debugInfo) {
auto data = Native::data<DOMNode>(this_);
if (!data->node()) {
return this_->toArray();
}
return domdocumenttype_properties_map.debugInfo(Object{this_});
}
///////////////////////////////////////////////////////////////////////////////
static Variant dom_element_tag_name_read(const Object& obj) {
CHECK_NODE(nodep);
xmlChar *qname;
xmlNsPtr ns = nodep->ns;
if (ns != nullptr && ns->prefix) {
qname = xmlStrdup(ns->prefix);
qname = xmlStrcat(qname, (xmlChar *)":");
qname = xmlStrcat(qname, nodep->name);
String ret((char *)qname, CopyString);
xmlFree(qname);
return ret;
}
return String((char *)nodep->name, CopyString);
}
static Variant dom_element_schema_type_info_read(const Object& /*obj*/) {
return init_null();
}
static DOMPropertyAccessor domelement_properties[] = {
{ "tagName", dom_element_tag_name_read, nullptr},
{ "schemaTypeInfo", dom_element_schema_type_info_read, nullptr},
{ nullptr, nullptr, nullptr}
};
static DOMPropertyAccessorMap domelement_properties_map
((DOMPropertyAccessor*)domelement_properties, &domnode_properties_map);
///////////////////////////////////////////////////////////////////////////////
struct DOMElementPropHandler : public DOMPropHandler<DOMElementPropHandler> {
static constexpr DOMPropertyAccessorMap& map = domelement_properties_map;
};
void HHVM_METHOD(DOMElement, __construct,
const String& name,
const Variant& value /* = null_string */,
const Variant& namespaceuri /*= null_string*/) {
auto* data = Native::data<DOMElement>(this_);
xmlNodePtr nodep = nullptr;
char *localname = nullptr, *prefix = nullptr;
int errorcode = 0;
xmlNsPtr nsptr = nullptr;
int name_valid = xmlValidateName((xmlChar *) name.data(), 0);
if (name_valid != 0) {
php_dom_throw_error(INVALID_CHARACTER_ERR, 1);
return;
}
/* Namespace logic is separate and only when uri passed in to insure
no BC breakage */
const String& str_namespaceuri = namespaceuri.isNull()
? null_string
: namespaceuri.toString();
if (!str_namespaceuri.empty()) {
errorcode = dom_check_qname(name.data(), &localname, &prefix,
str_namespaceuri.size(), name.size());
if (errorcode == 0) {
nodep = xmlNewNode (nullptr, (xmlChar *)localname);
if (nodep != nullptr && !str_namespaceuri.empty()) {
nsptr = dom_get_ns(nodep, str_namespaceuri.data(), &errorcode, prefix);
xmlSetNs(nodep, nsptr);
}
}
xmlFree(localname);
if (prefix != nullptr) {
xmlFree(prefix);
}
if (errorcode != 0) {
if (nodep != nullptr) {
xmlFreeNode(nodep);
}
php_dom_throw_error((dom_exception_code)errorcode, 1);
return;
}
} else {
/* If you don't pass a namespace uri, then you can't set a prefix */
localname = (char*)xmlSplitQName2((xmlChar *)name.data(),
(xmlChar **)&prefix);
if (prefix != nullptr) {
xmlFree(localname);
xmlFree(prefix);
php_dom_throw_error(NAMESPACE_ERR, 1);
return;
}
nodep = xmlNewNode(nullptr, (xmlChar *) name.data());
}
if (!nodep) {
php_dom_throw_error(INVALID_STATE_ERR, 1);
return;
}
const String& str_value = value.isNull() ? null_string : value.toString();
if (!str_value.empty()) {
xmlNodeSetContentLen(nodep, (xmlChar *)str_value.data(), str_value.size());
}
data->setNode(nodep);
}
Array HHVM_METHOD(DOMElement, __debugInfo) {
auto* data = Native::data<DOMElement>(this_);
if (!data->node()) {
return this_->toArray();
}
return domelement_properties_map.debugInfo(Object{this_});
}
String HHVM_METHOD(DOMElement, getAttribute,
const String& name) {
auto* data = Native::data<DOMElement>(this_);
xmlNodePtr nodep = data->nodep();
if (!nodep) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return empty_string();
}
xmlChar *value = nullptr;
xmlNodePtr attr;
attr = dom_get_dom1_attribute(nodep, (xmlChar*)name.data());
if (attr) {
switch (attr->type) {
case XML_ATTRIBUTE_NODE:
value = xmlNodeListGetString(attr->doc, attr->children, 1);
break;
case XML_NAMESPACE_DECL:
value = xmlStrdup(((xmlNsPtr)attr)->href);
break;
default:
value = xmlStrdup(((xmlAttributePtr)attr)->defaultValue);
}
}
if (value) {
String ret((char*)value, CopyString);
xmlFree(value);
return ret;
}
return empty_string();
}
Variant HHVM_METHOD(DOMElement, getAttributeNode,
const String& name) {
auto data = Native::data<DOMElement>(this_);
xmlNodePtr nodep = data->nodep();
if (!nodep) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
xmlNodePtr attrp;
attrp = dom_get_dom1_attribute(nodep, (xmlChar*)name.data());
if (attrp == nullptr) {
return false;
}
if (attrp->type == XML_NAMESPACE_DECL) {
xmlNsPtr curns;
//xmlNodePtr nsparent;
//nsparent = attrp->_private;
curns = xmlNewNs(nullptr, attrp->name, nullptr);
if (attrp->children) {
curns->prefix = xmlStrdup((xmlChar*)attrp->children);
}
if (attrp->children) {
attrp = xmlNewDocNode(nodep->doc, nullptr, (xmlChar *) attrp->children,
attrp->name);
} else {
attrp = xmlNewDocNode(nodep->doc, nullptr, (xmlChar *)"xmlns",
attrp->name);
}
attrp->type = XML_NAMESPACE_DECL;
//attrp->parent = nsparent;
attrp->ns = curns;
}
return php_dom_create_object((xmlNodePtr)attrp, data->doc());
}
Object HHVM_METHOD(DOMElement, getAttributeNodeNS,
const String& namespaceuri,
const String& localname) {
auto* data = Native::data<DOMElement>(this_);
xmlNodePtr elemp = data->nodep();
if (!elemp) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return Object();
}
xmlAttrPtr attrp;
attrp = xmlHasNsProp(elemp, (xmlChar*)localname.data(),
(xmlChar*)namespaceuri.data());
if (attrp == nullptr) {
return Object();
}
auto ret = php_dom_create_object((xmlNodePtr)attrp, data->doc());
if (ret.isObject()) return ret.toObject();
return Object();
}
String HHVM_METHOD(DOMElement, getAttributeNS,
const String& namespaceuri,
const String& localname) {
auto* data = Native::data<DOMElement>(this_);
xmlNodePtr elemp = data->nodep();
if (!elemp) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return empty_string();
}
xmlNsPtr nsptr;
xmlChar *strattr;
strattr = xmlGetNsProp(elemp, (xmlChar*)localname.data(),
(xmlChar*)namespaceuri.data());
if (strattr != nullptr) {
String ret((char*)strattr, CopyString);
xmlFree(strattr);
return ret;
} else {
if (xmlStrEqual((xmlChar*)namespaceuri.data(),
(xmlChar*)DOM_XMLNS_NAMESPACE)) {
nsptr = dom_get_nsdecl(elemp, (xmlChar*)localname.data());
if (nsptr != nullptr) {
return String((char*)nsptr->href, CopyString);
}
}
}
return empty_string();
}
Object HHVM_METHOD(DOMElement, getElementsByTagName,
const String& name) {
auto* data = Native::data<DOMElement>(this_);
return newDOMNodeList(data->doc(), Object{this_}, 0, name);
}
Object HHVM_METHOD(DOMElement, getElementsByTagNameNS,
const String& namespaceuri,
const String& localname) {
auto* data = Native::data<DOMElement>(this_);
return newDOMNodeList(data->doc(), Object{this_}, 0, localname,
namespaceuri);
}
bool HHVM_METHOD(DOMElement, hasAttribute,
const String& name) {
auto* data = Native::data<DOMElement>(this_);
xmlNodePtr nodep = data->nodep();
if (!nodep) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
return dom_get_dom1_attribute(nodep, (xmlChar*)name.data()) != nullptr;
}
bool HHVM_METHOD(DOMElement, hasAttributeNS,
const String& namespaceuri,
const String& localname) {
auto* data = Native::data<DOMElement>(this_);
xmlNodePtr elemp = data->nodep();
if (!elemp) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
xmlNs *nsp;
xmlChar *value = xmlGetNsProp(elemp, (xmlChar*)localname.data(),
(xmlChar*)namespaceuri.data());
if (value != nullptr) {
xmlFree(value);
return true;
} else {
if (xmlStrEqual((xmlChar*)namespaceuri.data(),
(xmlChar*)DOM_XMLNS_NAMESPACE)) {
nsp = dom_get_nsdecl(elemp, (xmlChar*)localname.data());
if (nsp != nullptr) {
return true;
}
}
}
return false;
}
bool HHVM_METHOD(DOMElement, removeAttribute,
const String& name) {
auto* data = Native::data<DOMElement>(this_);
xmlNodePtr nodep = data->nodep();
if (!nodep) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
xmlNodePtr attrp;
bool stricterror = data->doc() ? data->doc()->m_stricterror : true;
if (dom_node_is_read_only(nodep)) {
php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
return false;
}
attrp = dom_get_dom1_attribute(nodep, (xmlChar*)name.data());
if (attrp == nullptr) {
return false;
}
switch (attrp->type) {
case XML_ATTRIBUTE_NODE:
libxml_register_node(attrp)->unlink(); // release attrp if unused
break;
case XML_NAMESPACE_DECL:
return false;
default:
break;
}
return true;
}
Variant HHVM_METHOD(DOMElement, removeAttributeNode,
const Object& oldattr) {
auto* data = Native::data<DOMElement>(this_);
xmlNodePtr nodep = data->nodep();
if (!nodep) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
auto attr = getDOMNode(oldattr);
xmlAttrPtr attrp = (xmlAttrPtr)attr->nodep();
if (!attrp) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
bool stricterror = data->doc() ? data->doc()->m_stricterror : true;
if (dom_node_is_read_only(nodep)) {
php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
return false;
}
if (attrp->type != XML_ATTRIBUTE_NODE || attrp->parent != nodep) {
php_dom_throw_error(NOT_FOUND_ERR, data->doc()->m_stricterror);
return false;
}
auto attrNode = libxml_register_node((xmlNodePtr)attrp);
xmlUnlinkNode((xmlNodePtr)attrp);
return php_dom_create_object((xmlNodePtr)attrp, data->doc());
}
Variant HHVM_METHOD(DOMElement, removeAttributeNS,
const String& namespaceuri,
const String& localname) {
auto* data = Native::data<DOMElement>(this_);
xmlNodePtr nodep = data->nodep();
if (!nodep) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
xmlAttr *attrp;
xmlNsPtr nsptr;
bool stricterror = data->doc() ? data->doc()->m_stricterror : true;
if (dom_node_is_read_only(nodep)) {
php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
return init_null();
}
attrp = xmlHasNsProp(nodep, (xmlChar*)localname.data(),
(xmlChar*)namespaceuri.data());
nsptr = dom_get_nsdecl(nodep, (xmlChar*)localname.data());
if (nsptr != nullptr) {
if (xmlStrEqual((xmlChar*)namespaceuri.data(), nsptr->href)) {
if (nsptr->href != nullptr) {
xmlFree((char*)nsptr->href);
nsptr->href = nullptr;
}
if (nsptr->prefix != nullptr) {
xmlFree((char*)nsptr->prefix);
nsptr->prefix = nullptr;
}
} else {
return init_null();
}
}
if (attrp && attrp->type != XML_ATTRIBUTE_DECL) {
// release attrp if unused
libxml_register_node((xmlNodePtr)attrp)->unlink();
}
return init_null();
}
Variant HHVM_METHOD(DOMElement, setAttribute,
const String& name,
const String& value) {
auto* data = Native::data<DOMElement>(this_);
xmlNodePtr nodep = data->nodep();
if (!nodep) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
xmlNodePtr attr = nullptr;
int name_valid;
if (name.empty()) {
raise_warning("Attribute Name is required");
return false;
}
name_valid = xmlValidateName((xmlChar*)name.data(), 0);
if (name_valid != 0) {
php_dom_throw_error(INVALID_CHARACTER_ERR, 1);
return false;
}
bool stricterror = data->doc() ? data->doc()->m_stricterror : true;
if (dom_node_is_read_only(nodep)) {
php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
return false;
}
attr = dom_get_dom1_attribute(nodep, (xmlChar*)name.data());
if (attr != nullptr) {
switch (attr->type) {
case XML_ATTRIBUTE_NODE:
break;
case XML_NAMESPACE_DECL:
return false;
default:
break;
}
}
if (xmlStrEqual((xmlChar*)name.data(), (xmlChar*)"xmlns")) {
if (xmlNewNs(nodep, (xmlChar*)value.data(), nullptr)) {
return true;
}
} else {
attr = (xmlNodePtr)xmlSetProp(nodep, (xmlChar*)name.data(),
(xmlChar*)value.data());
}
if (!attr) {
raise_warning("No such attribute '%s'", name.data());
return false;
}
return php_dom_create_object(attr, data->doc());
}
Variant HHVM_METHOD(DOMElement, setAttributeNode,
const Object& newattr) {
auto* data = Native::data<DOMElement>(this_);
xmlNodePtr nodep = data->nodep();
if (!nodep) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
auto domattr = getDOMNode(newattr);
xmlAttrPtr attrp = (xmlAttrPtr)domattr->nodep();
if (!attrp) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
xmlAttr *existattrp = nullptr;
bool stricterror = data->doc() ? data->doc()->m_stricterror : true;
if (dom_node_is_read_only(nodep)) {
php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
return false;
}
if (attrp->type != XML_ATTRIBUTE_NODE) {
raise_warning("Attribute node is required");
return false;
}
if (!(attrp->doc == nullptr || attrp->doc == nodep->doc)) {
php_dom_throw_error(WRONG_DOCUMENT_ERR, data->doc()->m_stricterror);
return false;
}
existattrp = xmlHasProp(nodep, attrp->name);
if (existattrp != nullptr && existattrp->type != XML_ATTRIBUTE_DECL) {
xmlUnlinkNode((xmlNodePtr)existattrp);
}
if (attrp->parent != nullptr) {
xmlUnlinkNode((xmlNodePtr)attrp);
}
xmlAddChild(nodep, (xmlNodePtr)attrp);
/* Returns old property if removed otherwise null */
if (existattrp != nullptr) {
return php_dom_create_object((xmlNodePtr)existattrp, data->doc());
}
return init_null();
}
Variant HHVM_METHOD(DOMElement, setAttributeNodeNS,
const Object& newattr) {
auto* data = Native::data<DOMElement>(this_);
xmlNs *nsp;
xmlAttr *existattrp = nullptr;
xmlNodePtr nodep = data->nodep();
if (!nodep) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
auto domattr = getDOMNode(newattr);
xmlAttrPtr attrp = (xmlAttrPtr)domattr->nodep();
if (!attrp) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
bool stricterror = data->doc() ? data->doc()->m_stricterror : true;
if (dom_node_is_read_only(nodep)) {
php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
return false;
}
if (attrp->type != XML_ATTRIBUTE_NODE) {
raise_warning("Attribute node is required");
return false;
}
if (!(attrp->doc == nullptr || attrp->doc == nodep->doc)) {
php_dom_throw_error(WRONG_DOCUMENT_ERR, data->doc()->m_stricterror);
return false;
}
nsp = attrp->ns;
if (nsp != nullptr) {
existattrp = xmlHasNsProp(nodep, nsp->href, attrp->name);
} else {
existattrp = xmlHasProp(nodep, attrp->name);
}
if (existattrp != nullptr && existattrp->type != XML_ATTRIBUTE_DECL) {
xmlUnlinkNode((xmlNodePtr)existattrp);
}
if (attrp->parent != nullptr) {
xmlUnlinkNode((xmlNodePtr)attrp);
}
xmlAddChild(nodep, (xmlNodePtr) attrp);
/* Returns old property if removed otherwise null */
if (existattrp != nullptr) {
return php_dom_create_object((xmlNodePtr)existattrp, data->doc());
}
return init_null();
}
Variant HHVM_METHOD(DOMElement, setAttributeNS,
const String& namespaceuri,
const String& name,
const String& value) {
auto* data = Native::data<DOMElement>(this_);
xmlNodePtr elemp = data->nodep();
if (!elemp) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
xmlNsPtr nsptr;
char *localname = nullptr, *prefix = nullptr;
int errorcode = 0, is_xmlns = 0, name_valid;
if (name.empty()) {
raise_warning("Attribute Name is required");
return false;
}
bool stricterror = data->doc() ? data->doc()->m_stricterror : true;
if (dom_node_is_read_only(elemp)) {
php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
return init_null();
}
errorcode = dom_check_qname((char*)name.data(), &localname, &prefix,
namespaceuri.size(), name.size());
if (errorcode == 0) {
if (namespaceuri.size() > 0) {
if ((xmlStrEqual((xmlChar *) prefix, (xmlChar *)"xmlns") ||
(prefix == nullptr &&
xmlStrEqual((xmlChar *) localname, (xmlChar *)"xmlns"))) &&
xmlStrEqual((xmlChar *) namespaceuri.data(),
(xmlChar *)DOM_XMLNS_NAMESPACE)) {
is_xmlns = 1;
if (prefix == nullptr) {
nsptr = dom_get_nsdecl(elemp, nullptr);
} else {
nsptr = dom_get_nsdecl(elemp, (xmlChar *)localname);
}
} else {
nsptr = xmlSearchNsByHref(elemp->doc, elemp,
(xmlChar*)namespaceuri.data());
if (nsptr && nsptr->prefix == nullptr) {
xmlNsPtr tmpnsptr;
tmpnsptr = nsptr->next;
while (tmpnsptr) {
if ((tmpnsptr->prefix != nullptr) && (tmpnsptr->href != nullptr) &&
(xmlStrEqual(tmpnsptr->href, (xmlChar*)namespaceuri.data()))) {
nsptr = tmpnsptr;
break;
}
tmpnsptr = tmpnsptr->next;
}
if (tmpnsptr == nullptr) {
nsptr = _dom_new_reconNs(elemp->doc, elemp, nsptr);
}
}
}
if (nsptr == nullptr) {
if (prefix == nullptr) {
if (is_xmlns == 1) {
xmlNewNs(elemp, (xmlChar *)value.data(), nullptr);
xmlReconciliateNs(elemp->doc, elemp);
} else {
errorcode = NAMESPACE_ERR;
}
} else {
if (is_xmlns == 1) {
xmlNewNs(elemp, (xmlChar*)value.data(), (xmlChar*)localname);
} else {
nsptr = dom_get_ns(elemp, (char*)namespaceuri.data(),
&errorcode, prefix);
}
xmlReconciliateNs(elemp->doc, elemp);
}
} else {
if (is_xmlns == 1) {
if (nsptr->href) {
xmlFree((xmlChar*)nsptr->href);
}
nsptr->href = xmlStrdup((xmlChar*)value.data());
}
}
if (errorcode == 0 && is_xmlns == 0) {
xmlSetNsProp(elemp, nsptr, (xmlChar*)localname,
(xmlChar*)value.data());
}
} else {
name_valid = xmlValidateName((xmlChar*)localname, 0);
if (name_valid != 0) {
errorcode = INVALID_CHARACTER_ERR;
stricterror = 1;
} else {
xmlSetProp(elemp, (xmlChar*)localname, (xmlChar*)value.data());
}
}
}
xmlFree(localname);
if (prefix != nullptr) {
xmlFree(prefix);
}
if (errorcode != 0) {
php_dom_throw_error((dom_exception_code)errorcode, stricterror);
}
return init_null();
}
Variant HHVM_METHOD(DOMElement, setIDAttribute,
const String& name,
bool isid) {
auto* data = Native::data<DOMElement>(this_);
xmlNodePtr nodep = data->nodep();
if (!nodep) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return init_null();
}
xmlAttrPtr attrp;
bool stricterror = data->doc() ? data->doc()->m_stricterror : true;
if (dom_node_is_read_only(nodep)) {
php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
return init_null();
}
attrp = xmlHasNsProp(nodep, (xmlChar*)name.data(), nullptr);
if (attrp == nullptr || attrp->type == XML_ATTRIBUTE_DECL) {
php_dom_throw_error(NOT_FOUND_ERR, data->doc()->m_stricterror);
} else {
php_set_attribute_id(attrp, isid);
}
return init_null();
}
Variant HHVM_METHOD(DOMElement, setIDAttributeNode,
const Object& idattr,
bool isid) {
auto* data = Native::data<DOMElement>(this_);
xmlNodePtr nodep = data->nodep();
if (!nodep) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return init_null();
}
auto domattr = getDOMNode(idattr);
xmlAttrPtr attrp = (xmlAttrPtr)domattr->nodep();
if (!attrp) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return init_null();
}
bool stricterror = data->doc() ? data->doc()->m_stricterror : true;
if (dom_node_is_read_only(nodep)) {
php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
return init_null();
}
if (attrp->parent != nodep) {
php_dom_throw_error(NOT_FOUND_ERR, data->doc()->m_stricterror);
} else {
php_set_attribute_id(attrp, isid);
}
return init_null();
}
Variant HHVM_METHOD(DOMElement, setIDAttributeNS,
const String& namespaceuri,
const String& localname,
bool isid) {
auto* data = Native::data<DOMElement>(this_);
xmlNodePtr elemp = data->nodep();
if (!elemp) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return init_null();
}
xmlAttrPtr attrp;
bool stricterror = data->doc() ? data->doc()->m_stricterror : true;
if (dom_node_is_read_only(elemp)) {
php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, stricterror);
return init_null();
}
attrp = xmlHasNsProp(elemp, (xmlChar*)localname.data(),
(xmlChar*)namespaceuri.data());
if (attrp == nullptr || attrp->type == XML_ATTRIBUTE_DECL) {
php_dom_throw_error(NOT_FOUND_ERR, data->doc()->m_stricterror);
} else {
php_set_attribute_id(attrp, isid);
}
return init_null();
}
///////////////////////////////////////////////////////////////////////////////
#define CHECK_ENTITY(nodep) \
auto domentity = getDOMNode(obj); \
xmlEntity *nodep = (xmlEntity*)(domentity ? domentity->nodep() \
: nullptr); \
if (nodep == nullptr) { \
php_dom_throw_error(INVALID_STATE_ERR, 0); \
return init_null(); \
} \
static Variant dom_entity_public_id_read(const Object& obj) {
CHECK_ENTITY(nodep);
if (nodep->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
return init_null();
}
return String((char *)(nodep->ExternalID), CopyString);
}
static Variant dom_entity_system_id_read(const Object& obj) {
CHECK_ENTITY(nodep);
if (nodep->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
return init_null();
}
return String((char *)(nodep->SystemID), CopyString);
}
static Variant dom_entity_notation_name_read(const Object& obj) {
CHECK_ENTITY(nodep);
if (nodep->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
return init_null();
}
char *content = (char*)xmlNodeGetContent((xmlNodePtr) nodep);
String ret(content, CopyString);
xmlFree(content);
return ret;
}
static Variant dom_entity_actual_encoding_read(const Object& /*obj*/) {
return init_null();
}
static void dom_entity_actual_encoding_write(const Object& /*obj*/,
const Variant& /*value*/) {
// do nothing
}
static Variant dom_entity_encoding_read(const Object& /*obj*/) {
return init_null();
}
static void
dom_entity_encoding_write(const Object& /*obj*/, const Variant& /*value*/) {
// do nothing
}
static Variant dom_entity_version_read(const Object& /*obj*/) {
return init_null();
}
static void
dom_entity_version_write(const Object& /*obj*/, const Variant& /*value*/) {
// do nothing
}
static DOMPropertyAccessor domentity_properties[] = {
{ "publicId", dom_entity_public_id_read, nullptr },
{ "systemId", dom_entity_system_id_read, nullptr },
{ "notationName", dom_entity_notation_name_read, nullptr },
{ "actualEncoding", dom_entity_actual_encoding_read,
dom_entity_actual_encoding_write },
{ "encoding", dom_entity_encoding_read,
dom_entity_encoding_write },
{ "version", dom_entity_version_read,
dom_entity_version_write },
{ nullptr, nullptr, nullptr}
};
static DOMPropertyAccessorMap domentity_properties_map
((DOMPropertyAccessor*)domentity_properties, &domnode_properties_map);
struct DOMEntityPropHandler : public DOMPropHandler<DOMEntityPropHandler> {
static constexpr DOMPropertyAccessorMap& map = domentity_properties_map;
};
///////////////////////////////////////////////////////////////////////////////
Array HHVM_METHOD(DOMEntity, __debugInfo) {
auto data = Native::data<DOMNode>(this_);
if (!data->node()) {
return this_->toArray();
}
return domentity_properties_map.debugInfo(Object{this_});
}
///////////////////////////////////////////////////////////////////////////////
void HHVM_METHOD(DOMEntityReference, __construct,
const String& name) {
auto data = Native::data<DOMNode>(this_);
int name_valid = xmlValidateName((xmlChar *)name.data(), 0);
if (name_valid != 0) {
php_dom_throw_error(INVALID_CHARACTER_ERR, 1);
return;
}
data->setNode(xmlNewReference(nullptr, (xmlChar*)name.data()));
if (!data->node()) {
php_dom_throw_error(INVALID_STATE_ERR, 1);
}
}
///////////////////////////////////////////////////////////////////////////////
#define CHECK_NOTATION(nodep) \
auto domnotation = getDOMNode(obj); \
xmlEntity *nodep = (xmlEntity*)(domnotation ? domnotation->nodep() \
: nullptr); \
if (nodep == nullptr) { \
php_dom_throw_error(INVALID_STATE_ERR, 0); \
return init_null(); \
} \
static Variant dom_notation_public_id_read(const Object& obj) {
CHECK_NOTATION(nodep);
if (nodep->ExternalID) {
return String((char *)(nodep->ExternalID), CopyString);
}
return empty_string_variant();
}
static Variant dom_notation_system_id_read(const Object& obj) {
CHECK_NOTATION(nodep);
if (nodep->SystemID) {
return String((char *)(nodep->SystemID), CopyString);
}
return empty_string_variant();
}
static DOMPropertyAccessor domnotation_properties[] = {
{ "publicId", dom_notation_public_id_read, nullptr },
{ "systemId", dom_notation_system_id_read, nullptr },
{ "nodeName", domnode_nodename_read, nullptr },
{ "nodeValue", domnode_nodevalue_read, domnode_nodevalue_write },
{ "attributes", domnode_attributes_read, nullptr },
{ nullptr, nullptr, nullptr}
};
static DOMPropertyAccessorMap domnotation_properties_map
((DOMPropertyAccessor*)domnotation_properties);
struct DOMNotationPropHandler : public DOMPropHandler<DOMNotationPropHandler> {
static constexpr DOMPropertyAccessorMap& map = domnotation_properties_map;
};
///////////////////////////////////////////////////////////////////////////////
Array HHVM_METHOD(DOMNotation, __debugInfo) {
auto* data = Native::data<DOMNode>(this_);
if (!data->node()) {
return this_->toArray();
}
return domnotation_properties_map.debugInfo(Object{this_});
}
///////////////////////////////////////////////////////////////////////////////
static Variant dom_processinginstruction_target_read(const Object& obj) {
CHECK_NODE(nodep);
return String((char *)(nodep->name), CopyString);
}
static Variant dom_processinginstruction_data_read(const Object& obj) {
CHECK_NODE(nodep);
xmlChar *content = xmlNodeGetContent(nodep);
if (content) {
String ret((char*)content, CopyString);
xmlFree(content);
return ret;
}
return empty_string_variant();
}
static void dom_processinginstruction_data_write(const Object& obj,
const Variant& value) {
CHECK_WRITE_NODE(nodep);
String svalue = value.toString();
xmlNodeSetContentLen(nodep, (xmlChar*)svalue.data(), svalue.size() + 1);
}
static DOMPropertyAccessor domprocessinginstruction_properties[] = {
{ "target", dom_processinginstruction_target_read, nullptr },
{ "data", dom_processinginstruction_data_read,
dom_processinginstruction_data_write },
{ nullptr, nullptr, nullptr}
};
static DOMPropertyAccessorMap domprocessinginstruction_properties_map
((DOMPropertyAccessor*)domprocessinginstruction_properties,
&domnode_properties_map);
struct DOMProcessingInstructionPropHandler :
public DOMPropHandler<DOMProcessingInstructionPropHandler> {
static constexpr DOMPropertyAccessorMap& map =
domprocessinginstruction_properties_map;
};
///////////////////////////////////////////////////////////////////////////////
void HHVM_METHOD(DOMProcessingInstruction, __construct,
const String& name,
const Variant& value /*= null_string*/) {
int name_valid = xmlValidateName((xmlChar *)name.data(), 0);
if (name_valid != 0) {
php_dom_throw_error(INVALID_CHARACTER_ERR, 1);
return;
}
auto* data = Native::data<DOMNode>(this_);
const String& str_value = value.isNull() ? null_string : value.toString();
data->setNode(xmlNewPI((xmlChar *)name.data(), (xmlChar *)str_value.data()));
if (!data->node()) {
php_dom_throw_error(INVALID_STATE_ERR, 1);
}
}
Array HHVM_METHOD(DOMProcessingInstruction, __debugInfo) {
auto* data = Native::data<DOMNode>(this_);
if (!data->node()) {
return this_->toArray();
}
return domprocessinginstruction_properties_map.debugInfo(Object{this_});
}
///////////////////////////////////////////////////////////////////////////////
static Variant dom_namednodemap_length_read(const Object& obj) {
auto objmap = Native::data<DOMIterable>(obj);
int count = 0;
if (objmap->m_nodetype == XML_NOTATION_NODE ||
objmap->m_nodetype == XML_ENTITY_NODE) {
if (objmap->m_ht) {
count = xmlHashSize(objmap->m_ht);
}
} else {
xmlNodePtr nodep = objmap->getBaseNodeData()->nodep();
if (nodep) {
xmlAttrPtr curnode = nodep->properties;
if (curnode) {
count++;
while (curnode->next != nullptr) {
count++;
curnode = curnode->next;
}
}
}
}
return count;
}
static DOMPropertyAccessor domnamednodemap_properties[] = {
{ "length", dom_namednodemap_length_read, nullptr },
{ nullptr, nullptr, nullptr}
};
static DOMPropertyAccessorMap domnamednodemap_properties_map
((DOMPropertyAccessor*)domnamednodemap_properties);
struct DOMNamedNodeMapPropHandler :
public DOMPropHandler<DOMNamedNodeMapPropHandler> {
static constexpr DOMPropertyAccessorMap& map = domnamednodemap_properties_map;
};
///////////////////////////////////////////////////////////////////////////////
Variant HHVM_METHOD(DOMNamedNodeMap, getNamedItem,
const String& name) {
auto* data = Native::data<DOMIterable>(this_);
xmlNodePtr itemnode = nullptr;
if (data->m_nodetype == XML_NOTATION_NODE
|| data->m_nodetype == XML_ENTITY_NODE) {
if (data->m_ht) {
if (data->m_nodetype == XML_ENTITY_NODE) {
itemnode = (xmlNodePtr)xmlHashLookup(data->m_ht, (xmlChar*)name.data());
} else {
xmlNotation *notep =
(xmlNotation *)xmlHashLookup(data->m_ht,
(xmlChar*)name.data());
if (notep) {
itemnode = create_notation(notep->name, notep->PublicID,
notep->SystemID);
}
}
}
} else {
xmlNodePtr nodep = data->getBaseNodeData()->nodep();
if (nodep) {
itemnode = (xmlNodePtr)xmlHasProp(nodep, (xmlChar*)name.data());
}
}
if (itemnode) {
Variant ret = php_dom_create_object(itemnode, data->m_doc);
if (ret.isNull()) {
raise_warning("Cannot create required DOM object");
return false;
}
return ret;
}
return init_null();
}
Variant HHVM_METHOD(DOMNamedNodeMap, getNamedItemNS,
const String& namespaceuri,
const String& localname) {
auto data = Native::data<DOMIterable>(this_);
xmlNodePtr itemnode = nullptr;
if (data->m_nodetype == XML_NOTATION_NODE
|| data->m_nodetype == XML_ENTITY_NODE) {
if (data->m_ht) {
if (data->m_nodetype == XML_ENTITY_NODE) {
itemnode = (xmlNodePtr)xmlHashLookup(data->m_ht,
(xmlChar*)localname.data());
} else {
xmlNotation *notep =
(xmlNotation *)xmlHashLookup(data->m_ht, (xmlChar*)localname.data());
if (notep) {
itemnode = create_notation(notep->name, notep->PublicID,
notep->SystemID);
}
}
}
} else {
xmlNodePtr nodep = data->getBaseNodeData()->nodep();
if (nodep) {
itemnode = (xmlNodePtr)xmlHasNsProp(nodep, (xmlChar*)localname.data(),
(xmlChar*)namespaceuri.data());
}
}
if (itemnode) {
Variant ret = php_dom_create_object(itemnode, data->m_doc);
if (ret.isNull()) {
raise_warning("Cannot create required DOM object");
return false;
}
return ret;
}
return init_null();
}
Variant HHVM_METHOD(DOMNamedNodeMap, item,
int64_t index) {
auto* data = Native::data<DOMIterable>(this_);
if (index >= 0) {
xmlNodePtr itemnode = nullptr;
if (data->m_nodetype == XML_NOTATION_NODE
|| data->m_nodetype == XML_ENTITY_NODE) {
if (data->m_ht) {
if (data->m_nodetype == XML_ENTITY_NODE) {
itemnode = php_dom_libxml_hash_iter(data->m_ht, index);
} else {
itemnode = php_dom_libxml_notation_iter(data->m_ht, index);
}
}
} else {
xmlNodePtr nodep = data->getBaseNodeData()->nodep();
if (nodep) {
xmlNodePtr curnode = (xmlNodePtr)nodep->properties;
int count = 0;
while (count < index && curnode != nullptr) {
count++;
curnode = (xmlNodePtr)curnode->next;
}
itemnode = curnode;
}
}
if (itemnode) {
Variant ret = php_dom_create_object(itemnode, data->m_doc);
if (ret.isNull()) {
raise_warning("Cannot create required DOM object");
return false;
}
return ret;
}
}
return init_null();
}
Array HHVM_METHOD(DOMNamedNodeMap, __debugInfo) {
return domnamednodemap_properties_map.debugInfo(Object{this_});
}
Variant HHVM_METHOD(DOMNamedNodeMap, getIterator) {
auto data = Native::data<DOMIterable>(this_);
Object ret{getDOMNodeIteratorClass()};
DOMNodeIterator* iter = Native::data<DOMNodeIterator>(ret);
iter->set_iterator(this_, data);
iter->setKeyIsNamed();
return ret;
}
///////////////////////////////////////////////////////////////////////////////
static Variant dom_nodelist_length_read(const Object& obj) {
auto objmap = Native::data<DOMIterable>(obj);
int count = 0;
if (objmap->m_ht) {
count = xmlHashSize(objmap->m_ht);
} else {
if (objmap->m_nodetype == DOM_NODESET) {
count = objmap->m_baseobjptr.size();
} else {
xmlNodePtr nodep = objmap->getBaseNodeData()->nodep();
if (nodep) {
if (objmap->m_nodetype == XML_ATTRIBUTE_NODE ||
objmap->m_nodetype == XML_ELEMENT_NODE) {
xmlNodePtr curnode = nodep->children;
if (curnode) {
count++;
while (curnode->next != nullptr) {
count++;
curnode = curnode->next;
}
}
} else {
if (nodep->type == XML_DOCUMENT_NODE ||
nodep->type == XML_HTML_DOCUMENT_NODE) {
nodep = xmlDocGetRootElement((xmlDoc *) nodep);
} else {
nodep = nodep->children;
}
dom_get_elements_by_tag_name_ns_raw
(nodep, objmap->m_ns.data(), objmap->m_local.data(), &count, -1);
}
}
}
}
return count;
}
static DOMPropertyAccessor domnodelist_properties[] = {
{ "length", dom_nodelist_length_read, nullptr },
{ nullptr, nullptr, nullptr}
};
static DOMPropertyAccessorMap domnodelist_properties_map
((DOMPropertyAccessor*)domnodelist_properties);
struct DOMNodeListPropHandler : public DOMPropHandler<DOMNodeListPropHandler> {
static constexpr DOMPropertyAccessorMap& map = domnodelist_properties_map;
};
///////////////////////////////////////////////////////////////////////////////
Array HHVM_METHOD(DOMNodeList, __debugInfo) {
return domnodelist_properties_map.debugInfo(Object{this_});
}
Variant HHVM_METHOD(DOMNodeList, item,
int64_t index) {
auto data = Native::data<DOMIterable>(this_);
xmlNodePtr itemnode = nullptr;
xmlNodePtr nodep, curnode;
int count = 0;
if (index >= 0) {
if (data->m_ht) {
if (data->m_nodetype == XML_ENTITY_NODE) {
itemnode = php_dom_libxml_hash_iter(data->m_ht, index);
} else {
itemnode = php_dom_libxml_notation_iter(data->m_ht, index);
}
} else {
if (data->m_nodetype == DOM_NODESET) {
if (data->m_baseobjptr.exists(index)) {
return data->m_baseobjptr[index];
}
} else if (!data->m_baseobj.isNull()) {
nodep = data->getBaseNodeData()->nodep();
if (nodep) {
if (data->m_nodetype == XML_ATTRIBUTE_NODE ||
data->m_nodetype == XML_ELEMENT_NODE) {
curnode = nodep->children;
while (count < index && curnode != nullptr) {
count++;
curnode = curnode->next;
}
itemnode = curnode;
} else {
if (nodep->type == XML_DOCUMENT_NODE ||
nodep->type == XML_HTML_DOCUMENT_NODE) {
nodep = xmlDocGetRootElement((xmlDoc *) nodep);
} else {
nodep = nodep->children;
}
itemnode = dom_get_elements_by_tag_name_ns_raw(nodep,
data->m_ns.data(), data->m_local.data(), &count, index);
}
}
}
}
if (itemnode) {
return create_node_object(itemnode, data->m_doc);
}
}
return Object();
}
Variant HHVM_METHOD(DOMNodeList, getIterator) {
auto data = Native::data<DOMIterable>(this_);
Object ret{getDOMNodeIteratorClass()};
DOMNodeIterator* iter = Native::data<DOMNodeIterator>(ret);
iter->set_iterator(this_, data);
return ret;
}
///////////////////////////////////////////////////////////////////////////////
Variant HHVM_METHOD(DOMImplementation, createDocument,
const Variant& namespaceuri /* = null_string */,
const Variant& qualifiedname /* = null_string */,
const Variant& doctypeobj /* = null_object */) {
xmlDoc *docp;
xmlNode *nodep;
xmlNsPtr nsptr = nullptr;
int errorcode = 0;
char *prefix = nullptr, *localname = nullptr;
xmlDtdPtr doctype = nullptr;
const String& str_namespaceuri = namespaceuri.isNull()
? null_string
: namespaceuri.toString();
const String& str_qualifiedname = qualifiedname.isNull()
? null_string
: qualifiedname.toString();
const Object& obj_doctypeobj = doctypeobj.isNull()
? null_object
: doctypeobj.toObject();
if (!obj_doctypeobj.isNull()) {
auto *domdoctype = getDOMNode(obj_doctypeobj);
doctype = (xmlDtdPtr)domdoctype->nodep();
if (!doctype) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
if (doctype->type == XML_DOCUMENT_TYPE_NODE) {
raise_warning("Invalid DocumentType object");
return false;
}
if (doctype->doc != nullptr) {
php_dom_throw_error(WRONG_DOCUMENT_ERR, 1);
return false;
}
}
if (str_qualifiedname.size() > 0) {
errorcode = dom_check_qname((char*)str_qualifiedname.data(),
(char**)&localname, (char**)&prefix, 1,
str_qualifiedname.size());
if (errorcode == 0 && str_namespaceuri.size() > 0) {
nsptr = xmlNewNs(nullptr, (xmlChar*)str_namespaceuri.data(),
(xmlChar*)prefix);
if (nsptr == nullptr) {
errorcode = NAMESPACE_ERR;
}
}
}
if (prefix != nullptr) {
xmlFree(prefix);
}
if (errorcode != 0) {
if (localname != nullptr) {
xmlFree(localname);
}
php_dom_throw_error((dom_exception_code)errorcode, 1);
return false;
}
/* currently letting libxml2 set the version string */
docp = xmlNewDoc(nullptr);
if (!docp) {
if (localname != nullptr) {
xmlFree(localname);
}
return false;
}
if (doctype != nullptr) {
docp->intSubset = doctype;
doctype->parent = docp;
doctype->doc = docp;
docp->children = (xmlNodePtr)doctype;
docp->last = (xmlNodePtr)doctype;
}
if (localname != nullptr) {
nodep = xmlNewDocNode(docp, nsptr, (xmlChar*)localname, nullptr);
if (!nodep) {
if (doctype != nullptr) {
docp->intSubset = nullptr;
doctype->parent = nullptr;
doctype->doc = nullptr;
docp->children = nullptr;
docp->last = nullptr;
}
xmlFreeDoc(docp);
xmlFree(localname);
/* Need some type of error here */
raise_warning("Unexpected Error");
return false;
}
nodep->nsDef = nsptr;
xmlDocSetRootElement(docp, nodep);
xmlFree(localname);
}
Object ret{getDOMDocumentClass()};
auto doc_data = Native::data<DOMNode>(ret);
doc_data->setNode((xmlNodePtr)docp);
if (doctype) {
libxml_register_node((xmlNodePtr)doctype)->setDoc(doc_data->doc());
}
return ret;
}
Variant HHVM_METHOD(DOMImplementation, createDocumentType,
const Variant& qualifiedname /* = null_string */,
const Variant& publicid /* = null_string */,
const Variant& systemid /* = null_string */) {
xmlDtd *doctype;
xmlChar *pch1 = nullptr, *pch2 = nullptr, *localname = nullptr;
xmlURIPtr uri;
const String& str_qualifiedname = qualifiedname.isNull()
? null_string
: qualifiedname.toString();
const String& str_publicid = publicid.isNull()
? null_string
: publicid.toString();
const String& str_systemid = systemid.isNull()
? null_string
: systemid.toString();
if (str_qualifiedname.empty()) {
raise_warning("qualifiedname is required");
return false;
}
if (str_publicid.size() > 0) {
pch1 = (xmlChar*)str_publicid.data();
}
if (str_systemid.size() > 0) {
pch2 = (xmlChar*)str_systemid.data();
}
uri = xmlParseURI((char*)str_qualifiedname.data());
if (uri != nullptr && uri->opaque != nullptr) {
localname = xmlStrdup((xmlChar*)uri->opaque);
if (xmlStrchr(localname, (xmlChar)':') != nullptr) {
php_dom_throw_error(NAMESPACE_ERR, 1);
xmlFreeURI(uri);
xmlFree(localname);
return false;
}
} else {
localname = xmlStrdup((xmlChar*)str_qualifiedname.data());
}
if (uri) {
xmlFreeURI(uri);
}
doctype = xmlCreateIntSubset(nullptr, localname, pch1, pch2);
xmlFree(localname);
if (doctype == nullptr) {
raise_warning("Unable to create DocumentType");
return false;
}
return create_node_object((xmlNodePtr)doctype);
}
bool HHVM_METHOD(DOMImplementation, hasFeature,
const String& feature,
const String& version) {
return dom_has_feature(feature.data(), version.data());
}
///////////////////////////////////////////////////////////////////////////////
static Variant dom_xpath_document_read(const Object& obj) {
xmlDoc *docp = nullptr;
DOMXPath* xpath = getDOMXPath(obj);
xmlXPathContextPtr ctx = (xmlXPathContextPtr)xpath->m_node;
if (ctx) {
docp = (xmlDocPtr)ctx->doc;
}
auto doc_data = Native::data<DOMNode>(xpath->m_doc);
// If document in the context is the same, return it.
if ((xmlDocPtr)doc_data->nodep() == docp) {
return xpath->m_doc;
}
// Otherwise, create a new doc.
return create_node_object((xmlNodePtr)docp, xpath->m_doc);
}
static DOMPropertyAccessor domxpath_properties[] = {
{ "document", dom_xpath_document_read, nullptr },
{ nullptr, nullptr, nullptr}
};
static DOMPropertyAccessorMap domxpath_properties_map
((DOMPropertyAccessor*)domxpath_properties);
struct DOMXPathPropHandler : public DOMPropHandler<DOMXPathPropHandler> {
static constexpr DOMPropertyAccessorMap& map = domxpath_properties_map;
};
///////////////////////////////////////////////////////////////////////////////
static void dom_xpath_ext_function_php(xmlXPathParserContextPtr ctxt,
int nargs, int type) {
int error = 0;
DOMXPath* intern = (DOMXPath*) ctxt->context->userData;
if (intern == nullptr) {
xmlGenericError(xmlGenericErrorContext,
"xmlExtFunctionTest: failed to get the internal object\n");
error = 1;
} else if (intern->m_registerPhpFunctions == 0) {
xmlGenericError(xmlGenericErrorContext,
"xmlExtFunctionTest: PHP Object did not register "
"PHP functions\n");
error = 1;
}
xmlXPathObjectPtr obj;
if (error == 1) {
for (int i = nargs - 1; i >= 0; i--) {
obj = valuePop(ctxt);
xmlXPathFreeObject(obj);
}
return;
}
Array args_vec = Array::CreateVec();
for (int i = nargs - 2; i >= 0; i--) {
Variant arg;
obj = valuePop(ctxt);
switch (obj->type) {
case XPATH_STRING:
arg = String((char *)obj->stringval, CopyString);
break;
case XPATH_BOOLEAN:
arg = (bool)obj->boolval;
break;
case XPATH_NUMBER:
arg = (double)obj->floatval;
break;
case XPATH_NODESET:
if (type == 1) {
char *str = (char *)xmlXPathCastToString(obj);
arg = String(str, CopyString);
xmlFree(str);
} else if (type == 2) {
Array argArr = Array::CreateVec();
if (obj->nodesetval && obj->nodesetval->nodeNr > 0) {
for (int j = 0; j < obj->nodesetval->nodeNr; j++) {
xmlNodePtr node = obj->nodesetval->nodeTab[j];
/* not sure, if we need this... it's copied from xpath.c */
if (node->type == XML_NAMESPACE_DECL) {
// xmlNodePtr nsparent = (xmlNodePtr)node->_private;
xmlNodePtr nsparent = nullptr;
xmlNsPtr curns = xmlNewNs(nullptr, node->name, nullptr);
if (node->children) {
curns->prefix = xmlStrdup((xmlChar *)node->children);
}
if (node->children) {
node = xmlNewDocNode(node->doc, nullptr,
(xmlChar *)node->children, node->name);
} else {
node = xmlNewDocNode(node->doc, nullptr,
(xmlChar *)"xmlns", node->name);
}
node->type = XML_NAMESPACE_DECL;
node->parent = nsparent;
node->ns = curns;
}
argArr.append(create_node_object(node, intern->m_doc));
}
}
arg = Variant(argArr);
}
break;
default:
arg = String((char *)xmlXPathCastToString(obj), CopyString);
}
xmlXPathFreeObject(obj);
args_vec.append(arg);
}
/* Reverse order to pop values off ctxt stack */
Array args;
for (auto i = args_vec.size(); i > 0; i--) {
args.append(args_vec.lookup(safe_cast<int64_t>(i - 1)));
}
obj = valuePop(ctxt);
if (obj->stringval == nullptr) {
xmlXPathFreeObject(obj);
raise_warning("Handler name must be a string");
return;
}
String handler((char*)obj->stringval, CopyString);
xmlXPathFreeObject(obj);
if (!is_callable(handler)) {
raise_warning("Unable to call handler %s()", handler.data());
} else if (intern->m_registerPhpFunctions == 2 &&
!intern->m_registered_phpfunctions.exists(handler)) {
/* Push an empty string, so that we at least have an xslt result... */
valuePush(ctxt, xmlXPathNewString((xmlChar *)""));
raise_warning("Not allowed to call handler '%s()'.", handler.data());
} else {
Variant retval = vm_call_user_func(handler, args);
if (retval.isObject() &&
retval.getObjectData()->instanceof(s_DOMNode)) {
if (intern->m_node_list.empty()) {
intern->m_node_list = Array::CreateVec();
}
intern->m_node_list.append(retval);
auto* node_data = Native::data<DOMNode>(retval.toObject());
xmlNode *nodep = node_data->nodep();
if (nodep) valuePush(ctxt, xmlXPathNewNodeSet(nodep));
} else if (retval.is(KindOfBoolean)) {
valuePush(ctxt, xmlXPathNewBoolean(retval.toBoolean()));
} else if (retval.isObject()) {
valuePush(ctxt, xmlXPathNewString((xmlChar *)""));
raise_warning("A PHP Object cannot be converted to an XPath-string");
} else {
String sretval = retval.toString();
valuePush(ctxt, xmlXPathNewString((xmlChar*)sretval.data()));
}
}
}
static void dom_xpath_ext_function_string_php(xmlXPathParserContextPtr ctxt,
int nargs) {
dom_xpath_ext_function_php(ctxt, nargs, 1);
}
static void dom_xpath_ext_function_object_php(xmlXPathParserContextPtr ctxt,
int nargs) {
dom_xpath_ext_function_php(ctxt, nargs, 2);
}
void DOMXPath::sweep() {
if (m_node) {
xmlXPathFreeContext((xmlXPathContextPtr)m_node);
m_node = nullptr;
}
}
void HHVM_METHOD(DOMXPath, __construct,
const Variant& doc) {
auto* data = Native::data<DOMXPath>(this_);
data->m_doc = doc.toObject();
if (!data->m_doc->instanceof(getDOMNodeClass())) {
SystemLib::throwExceptionObject(String("DOMXPath::__construct expects "
"parameter 1 to be DOMNode"));
return;
}
auto doc_data = Native::data<DOMNode>(data->m_doc);
xmlDocPtr docp = (xmlDocPtr)doc_data->nodep();
xmlXPathContextPtr ctx = xmlXPathNewContext(docp);
if (ctx == nullptr) {
php_dom_throw_error(INVALID_STATE_ERR, 1);
return;
}
xmlXPathRegisterFuncNS(ctx, (const xmlChar *) "functionString",
(const xmlChar *) "http://php.net/xpath",
dom_xpath_ext_function_string_php);
xmlXPathRegisterFuncNS(ctx, (const xmlChar *) "function",
(const xmlChar *) "http://php.net/xpath",
dom_xpath_ext_function_object_php);
data->m_node = ctx;
ctx->userData = data;
}
Array HHVM_METHOD(DOMXPath, __debugInfo) {
auto* data = Native::data<DOMXPath>(this_);
if (!data->m_node) {
return this_->toArray();
}
return domxpath_properties_map.debugInfo(Object{this_});
}
Variant HHVM_METHOD(DOMXPath, evaluate,
const String& expr,
const Variant& context /* = null_object */,
bool registerNodeNS /* = true */) {
auto* data = Native::data<DOMXPath>(this_);
const Object& obj_context = context.isNull()
? null_object
: context.toObject();
VMRegGuard _;
return php_xpath_eval(data, expr, obj_context, PHP_DOM_XPATH_EVALUATE,
registerNodeNS);
}
Variant HHVM_METHOD(DOMXPath, query,
const String& expr,
const Variant& context /* = null_object */,
bool registerNodeNS /* = true */) {
auto* data = Native::data<DOMXPath>(this_);
const Object& obj_context = context.isNull()
? null_object
: context.toObject();
VMRegGuard _;
return php_xpath_eval(data, expr, obj_context, PHP_DOM_XPATH_QUERY,
registerNodeNS);
}
bool HHVM_METHOD(DOMXPath, registerNamespace,
const String& prefix,
const String& uri) {
auto* data = Native::data<DOMXPath>(this_);
xmlXPathContextPtr ctxp = (xmlXPathContextPtr)data->m_node;
if (ctxp == nullptr) {
raise_warning("Invalid XPath Context");
return false;
}
return xmlXPathRegisterNs(ctxp, (xmlChar*)prefix.data(),
(xmlChar*)uri.data()) == 0;
}
Variant HHVM_METHOD(DOMXPath, registerPHPFunctions,
const Variant& funcs /* = null */) {
auto* data = Native::data<DOMXPath>(this_);
if (funcs.isArray()) {
Array arr = funcs.toArray();
for (ArrayIter iter(arr); iter; ++iter) {
data->m_registered_phpfunctions.set(iter.second(), "1");
}
data->m_registerPhpFunctions = 2;
return true;
}
if (funcs.isString()) {
data->m_registered_phpfunctions.set(funcs, "1");
data->m_registerPhpFunctions = 2;
} else {
data->m_registerPhpFunctions = 1;
}
return init_null();
}
///////////////////////////////////////////////////////////////////////////////
void DOMNodeIterator::reset_iterator() {
assertx(m_objmap);
xmlNodePtr curnode = nullptr;
if (m_objmap->m_nodetype != XML_ENTITY_NODE &&
m_objmap->m_nodetype != XML_NOTATION_NODE) {
m_index = -1;
if (m_objmap->m_nodetype == DOM_NODESET) {
m_iter = ArrayIter(m_objmap->m_baseobjptr);
} else {
xmlNodePtr nodep = m_objmap->getBaseNodeData()->nodep();
if (!nodep) {
goto err;
}
if (m_objmap->m_nodetype == XML_ATTRIBUTE_NODE ||
m_objmap->m_nodetype == XML_ELEMENT_NODE) {
if (m_objmap->m_nodetype == XML_ATTRIBUTE_NODE) {
curnode = (xmlNodePtr)nodep->properties;
} else {
curnode = (xmlNodePtr)nodep->children;
}
} else {
if (nodep->type == XML_DOCUMENT_NODE ||
nodep->type == XML_HTML_DOCUMENT_NODE) {
nodep = xmlDocGetRootElement((xmlDoc *) nodep);
} else {
nodep = nodep->children;
}
m_index = 0;
int previndex = 0;
curnode = dom_get_elements_by_tag_name_ns_raw
(nodep, m_objmap->m_ns.data(), m_objmap->m_local.data(),
&previndex, m_index);
}
}
++m_index;
} else {
if (m_objmap->m_nodetype == XML_ENTITY_NODE) {
curnode = php_dom_libxml_hash_iter(m_objmap->m_ht, 0);
} else {
curnode = php_dom_libxml_notation_iter(m_objmap->m_ht, 0);
}
m_index = 1;
}
err:
if (curnode) {
auto doc = m_objmap->getBaseNodeData()->doc();
m_curobj = create_node_object(curnode, doc).toObject();
} else {
m_curobj.reset();
}
}
void DOMNodeIterator::set_iterator(ObjectData* o, DOMIterable *objmap) {
m_o.reset(o);
m_objmap = objmap;
reset_iterator();
}
Variant HHVM_METHOD(DOMNodeIterator, current) {
auto* data = Native::data<DOMNodeIterator>(this_);
if (data->m_iter) {
return data->m_iter.second();
}
return data->m_curobj;
}
Variant HHVM_METHOD(DOMNodeIterator, key) {
auto* data = Native::data<DOMNodeIterator>(this_);
if (data->m_iter) {
return data->m_iter.first();
}
if (data->m_keyIsNamed) {
DOMNode* node_data = Native::data<DOMNode>(data->m_curobj);
xmlNodePtr curnode = node_data->nodep();
if (!curnode) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
return String((const char *)curnode->name, CopyString);
}
return data->m_index;
}
Variant HHVM_METHOD(DOMNodeIterator, next) {
auto* data = Native::data<DOMNodeIterator>(this_);
if (data->m_iter) {
data->m_iter.next();
return init_null();
}
XMLNode curnode = nullptr;
if (data->m_objmap->m_nodetype != XML_ENTITY_NODE &&
data->m_objmap->m_nodetype != XML_NOTATION_NODE) {
DOMNode* node_data = Native::data<DOMNode>(data->m_curobj);
curnode = node_data->node();
if (data->m_objmap->m_nodetype == XML_ATTRIBUTE_NODE ||
data->m_objmap->m_nodetype == XML_ELEMENT_NODE) {
if (!curnode->nodep()) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
curnode = libxml_register_node(curnode->nodep()->next);
} else {
/* Nav the tree evey time as this is LIVE */
xmlNodePtr basenode =
data->m_objmap->getBaseNodeData()->nodep();
if (!basenode) {
php_dom_throw_error(INVALID_STATE_ERR, 0);
return false;
}
if (basenode && (basenode->type == XML_DOCUMENT_NODE ||
basenode->type == XML_HTML_DOCUMENT_NODE)) {
basenode = xmlDocGetRootElement((xmlDoc *) basenode);
} else if (basenode) {
basenode = basenode->children;
} else {
goto err;
}
int previndex = 0;
curnode = libxml_register_node(dom_get_elements_by_tag_name_ns_raw
(basenode, data->m_objmap->m_ns.data(), data->m_objmap->m_local.data(),
&previndex, data->m_index));
}
++data->m_index;
} else {
if (data->m_objmap->m_nodetype == XML_ENTITY_NODE) {
curnode =
libxml_register_node(php_dom_libxml_hash_iter(data->m_objmap->m_ht,
data->m_index));
} else {
curnode =
libxml_register_node(php_dom_libxml_notation_iter(data->m_objmap->m_ht,
data->m_index));
}
++data->m_index;
}
err:
if (curnode && curnode->nodep()) {
auto doc = data->m_objmap->getBaseNodeData()->doc();
data->m_curobj = create_node_object(curnode->nodep(), doc).toObject();
} else {
data->m_curobj.reset();
}
return init_null();
}
Variant HHVM_METHOD(DOMNodeIterator, rewind) {
auto* data = Native::data<DOMNodeIterator>(this_);
data->m_iter.reset();
data->m_index = -1;
data->reset_iterator();
return init_null();
}
Variant HHVM_METHOD(DOMNodeIterator, valid) {
auto* data = Native::data<DOMNodeIterator>(this_);
if (data->m_iter) {
return !data->m_iter.end();
}
return !data->m_curobj.isNull();
}
///////////////////////////////////////////////////////////////////////////////
Variant HHVM_FUNCTION(dom_import_simplexml,
const Object& node) {
xmlNodePtr nodep = SimpleXMLElement_exportNode(node);
if (nodep && (nodep->type == XML_ELEMENT_NODE ||
nodep->type == XML_ATTRIBUTE_NODE)) {
return create_node_object(nodep);
} else {
raise_warning("Invalid Nodetype to import");
return init_null();
}
}
///////////////////////////////////////////////////////////////////////////////
struct DOMDocumentExtension final : Extension {
DOMDocumentExtension() : Extension("domdocument") {}
void moduleInit() override {
HHVM_ME(DOMNode, appendChild);
HHVM_ME(DOMNode, cloneNode);
HHVM_ME(DOMNode, getLineNo);
HHVM_ME(DOMNode, hasAttributes);
HHVM_ME(DOMNode, hasChildNodes);
HHVM_ME(DOMNode, insertBefore);
HHVM_ME(DOMNode, isDefaultNamespace);
HHVM_ME(DOMNode, isSameNode);
HHVM_ME(DOMNode, isSupported);
HHVM_ME(DOMNode, lookupNamespaceUri);
HHVM_ME(DOMNode, lookupPrefix);
HHVM_ME(DOMNode, normalize);
HHVM_ME(DOMNode, removeChild);
HHVM_ME(DOMNode, replaceChild);
HHVM_ME(DOMNode, C14N);
HHVM_ME(DOMNode, C14Nfile);
HHVM_ME(DOMNode, getNodePath);
HHVM_ME(DOMNode, __debugInfo);
Native::registerNativeDataInfo<DOMNode>(s_DOMNode.get(),
Native::NDIFlags::NO_SWEEP);
Native::registerNativePropHandler<DOMNodePropHandler>(s_DOMNode);
HHVM_ME(DOMAttr, __construct);
HHVM_ME(DOMAttr, isId);
HHVM_ME(DOMAttr, __debugInfo);
Native::registerNativePropHandler<DOMAttrPropHandler>(s_DOMAttr);
HHVM_ME(DOMCharacterData, appendData);
HHVM_ME(DOMCharacterData, deleteData);
HHVM_ME(DOMCharacterData, insertData);
HHVM_ME(DOMCharacterData, replaceData);
HHVM_ME(DOMCharacterData, substringData);
HHVM_ME(DOMCharacterData, __debugInfo);
Native::registerNativePropHandler<DOMCharacterDataPropHandler>(
s_DOMCharacterData);
HHVM_ME(DOMComment, __construct);
HHVM_ME(DOMText, __construct);
HHVM_ME(DOMText, isWhitespaceInElementContent);
HHVM_ME(DOMText, isElementContentWhitespace);
HHVM_ME(DOMText, splitText);
HHVM_ME(DOMText, __debugInfo);
Native::registerNativePropHandler<DOMTextPropHandler>(s_DOMText);
HHVM_ME(DOMCdataSection, __construct);
HHVM_ME(DOMDocument, __construct);
HHVM_ME(DOMDocument, createAttribute);
HHVM_ME(DOMDocument, createAttributeNS);
HHVM_ME(DOMDocument, createCDATASection);
HHVM_ME(DOMDocument, createComment);
HHVM_ME(DOMDocument, createDocumentFragment);
HHVM_ME(DOMDocument, createElement);
HHVM_ME(DOMDocument, createElementNS);
HHVM_ME(DOMDocument, createEntityReference);
HHVM_ME(DOMDocument, createProcessingInstruction);
HHVM_ME(DOMDocument, createTextNode);
HHVM_ME(DOMDocument, getElementById);
HHVM_ME(DOMDocument, getElementsByTagName);
HHVM_ME(DOMDocument, getElementsByTagNameNS);
HHVM_ME(DOMDocument, importNode);
HHVM_ME(DomDocument, _load);
HHVM_ME(DomDocument, _loadHTML);
HHVM_ME(DOMDocument, normalizeDocument);
HHVM_ME(DOMDocument, registerNodeClass);
HHVM_ME(DOMDocument, relaxNGValidate);
HHVM_ME(DOMDocument, relaxNGValidateSource);
HHVM_ME(DOMDocument, save);
HHVM_ME(DOMDocument, saveHTML);
HHVM_ME(DOMDocument, saveHTMLFile);
HHVM_ME(DOMDocument, saveXML);
HHVM_ME(DOMDocument, schemaValidate);
HHVM_ME(DOMDocument, schemaValidateSource);
HHVM_ME(DOMDocument, validate);
HHVM_ME(DOMDocument, xinclude);
HHVM_ME(DOMDocument, __debugInfo);
Native::registerNativePropHandler<DOMDocumentPropHandler>(s_DOMDocument);
HHVM_ME(DOMDocumentFragment, __construct);
HHVM_ME(DOMDocumentFragment, appendXML);
HHVM_ME(DOMDocumentType, __debugInfo);
Native::registerNativePropHandler<DOMDocumentTypePropHandler>(
s_DOMDocumentType);
HHVM_ME(DOMElement, __construct);
HHVM_ME(DOMElement, getAttribute);
HHVM_ME(DOMElement, getAttributeNode);
HHVM_ME(DOMElement, getAttributeNodeNS);
HHVM_ME(DOMElement, getAttributeNS);
HHVM_ME(DOMElement, getElementsByTagName);
HHVM_ME(DOMElement, getElementsByTagNameNS);
HHVM_ME(DOMElement, hasAttribute);
HHVM_ME(DOMElement, hasAttributeNS);
HHVM_ME(DOMElement, removeAttribute);
HHVM_ME(DOMElement, removeAttributeNode);
HHVM_ME(DOMElement, removeAttributeNS);
HHVM_ME(DOMElement, setAttribute);
HHVM_ME(DOMElement, setAttributeNode);
HHVM_ME(DOMElement, setAttributeNodeNS);
HHVM_ME(DOMElement, setAttributeNS);
HHVM_ME(DOMElement, setIDAttribute);
HHVM_ME(DOMElement, setIDAttributeNode);
HHVM_ME(DOMElement, setIDAttributeNS);
HHVM_ME(DOMElement, __debugInfo);
Native::registerNativeDataInfo<DOMElement>(s_DOMElement.get(),
Native::NDIFlags::NO_SWEEP);
Native::registerNativePropHandler<DOMElementPropHandler>(s_DOMElement);
HHVM_ME(DOMEntity, __debugInfo);
Native::registerNativePropHandler<DOMEntityPropHandler>(s_DOMEntity);
HHVM_ME(DOMEntityReference, __construct);
HHVM_ME(DOMNotation, __debugInfo);
Native::registerNativePropHandler<DOMNotationPropHandler>(s_DOMNotation);
HHVM_ME(DOMProcessingInstruction, __construct);
HHVM_ME(DOMProcessingInstruction, __debugInfo);
Native::registerNativePropHandler<DOMProcessingInstructionPropHandler>(
s_DOMProcessingInstruction);
HHVM_ME(DOMNodeIterator, current);
HHVM_ME(DOMNodeIterator, key);
HHVM_ME(DOMNodeIterator, next);
HHVM_ME(DOMNodeIterator, rewind);
HHVM_ME(DOMNodeIterator, valid);
Native::registerNativeDataInfo<DOMNodeIterator>(s_DOMNodeIterator.get(),
Native::NDIFlags::NO_SWEEP);
HHVM_ME(DOMNamedNodeMap, getNamedItem);
HHVM_ME(DOMNamedNodeMap, getNamedItemNS);
HHVM_ME(DOMNamedNodeMap, item);
HHVM_ME(DOMNamedNodeMap, getIterator);
Native::registerNativePropHandler<DOMNamedNodeMapPropHandler>(
s_DOMNamedNodeMap);
HHVM_ME(DOMNodeList, item);
HHVM_ME(DOMNodeList, getIterator);
HHVM_ME(DOMNodeList, __debugInfo);
Native::registerNativePropHandler<DOMNodeListPropHandler>(s_DOMNodeList);
Native::registerNativeDataInfo<DOMIterable>(
s_DOMIterable.get(), Native::NDIFlags::NO_SWEEP);
HHVM_ME(DOMImplementation, createDocument);
HHVM_ME(DOMImplementation, createDocumentType);
HHVM_ME(DOMImplementation, hasFeature);
HHVM_ME(DOMXPath, __construct);
HHVM_ME(DOMXPath, evaluate);
HHVM_ME(DOMXPath, query);
HHVM_ME(DOMXPath, registerNamespace);
HHVM_ME(DOMXPath, registerPHPFunctions);
HHVM_ME(DOMXPath, __debugInfo);
Native::registerNativeDataInfo<DOMXPath>(s_DOMXPath.get());
Native::registerNativePropHandler<DOMXPathPropHandler>(s_DOMXPath);
HHVM_FE(dom_import_simplexml);
HHVM_RC_INT_SAME(DOMSTRING_SIZE_ERR);
HHVM_RC_INT(DOM_HIERARCHY_REQUEST_ERR, HIERARCHY_REQUEST_ERR);
HHVM_RC_INT(DOM_INDEX_SIZE_ERR, INDEX_SIZE_ERR);
HHVM_RC_INT(DOM_INUSE_ATTRIBUTE_ERR, INUSE_ATTRIBUTE_ERR);
HHVM_RC_INT(DOM_INVALID_ACCESS_ERR, INVALID_ACCESS_ERR);
HHVM_RC_INT(DOM_INVALID_CHARACTER_ERR, INVALID_CHARACTER_ERR);
HHVM_RC_INT(DOM_INVALID_MODIFICATION_ERR, INVALID_MODIFICATION_ERR);
HHVM_RC_INT(DOM_INVALID_STATE_ERR, INVALID_STATE_ERR);
HHVM_RC_INT(DOM_NAMESPACE_ERR, NAMESPACE_ERR);
HHVM_RC_INT(DOM_NOT_FOUND_ERR, NOT_FOUND_ERR);
HHVM_RC_INT(DOM_NOT_SUPPORTED_ERR, NOT_SUPPORTED_ERR);
HHVM_RC_INT(DOM_NO_DATA_ALLOWED_ERR, NO_DATA_ALLOWED_ERR);
HHVM_RC_INT(DOM_NO_MODIFICATION_ALLOWED_ERR, NO_MODIFICATION_ALLOWED_ERR);
HHVM_RC_INT(DOM_PHP_ERR, PHP_ERR);
HHVM_RC_INT(DOM_SYNTAX_ERR, SYNTAX_ERR);
HHVM_RC_INT(DOM_VALIDATION_ERR, VALIDATION_ERR);
HHVM_RC_INT(DOM_WRONG_DOCUMENT_ERR, WRONG_DOCUMENT_ERR);
HHVM_RC_INT_SAME(XML_ELEMENT_NODE);
HHVM_RC_INT_SAME(XML_ATTRIBUTE_NODE);
HHVM_RC_INT_SAME(XML_TEXT_NODE);
HHVM_RC_INT_SAME(XML_CDATA_SECTION_NODE);
HHVM_RC_INT_SAME(XML_ENTITY_REF_NODE);
HHVM_RC_INT_SAME(XML_ENTITY_NODE);
HHVM_RC_INT_SAME(XML_PI_NODE);
HHVM_RC_INT_SAME(XML_COMMENT_NODE);
HHVM_RC_INT_SAME(XML_DOCUMENT_NODE);
HHVM_RC_INT_SAME(XML_DOCUMENT_TYPE_NODE);
HHVM_RC_INT_SAME(XML_DOCUMENT_FRAG_NODE);
HHVM_RC_INT_SAME(XML_NOTATION_NODE);
HHVM_RC_INT_SAME(XML_HTML_DOCUMENT_NODE);
HHVM_RC_INT_SAME(XML_DTD_NODE);
HHVM_RC_INT(XML_ELEMENT_DECL_NODE, XML_ELEMENT_DECL);
HHVM_RC_INT(XML_ATTRIBUTE_DECL_NODE, XML_ATTRIBUTE_DECL);
HHVM_RC_INT(XML_ENTITY_DECL_NODE, XML_ENTITY_DECL);
HHVM_RC_INT(XML_NAMESPACE_DECL_NODE, XML_NAMESPACE_DECL);
HHVM_RC_INT_SAME(XML_LOCAL_NAMESPACE);
#ifdef XML_GLOBAL_NAMESPACE
HHVM_RC_INT_SAME(XML_GLOBAL_NAMESPACE);
#endif
HHVM_RC_INT_SAME(XML_ATTRIBUTE_CDATA);
HHVM_RC_INT_SAME(XML_ATTRIBUTE_ID);
HHVM_RC_INT_SAME(XML_ATTRIBUTE_IDREF);
HHVM_RC_INT_SAME(XML_ATTRIBUTE_IDREFS);
HHVM_RC_INT(XML_ATTRIBUTE_ENTITY, XML_ATTRIBUTE_ENTITIES);
HHVM_RC_INT_SAME(XML_ATTRIBUTE_NMTOKEN);
HHVM_RC_INT_SAME(XML_ATTRIBUTE_NMTOKENS);
HHVM_RC_INT_SAME(XML_ATTRIBUTE_ENUMERATION);
HHVM_RC_INT_SAME(XML_ATTRIBUTE_NOTATION);
loadSystemlib();
}
} s_domdocument_extension;
}