Beispiel #1
0
Variant HHVM_METHOD(XMLReader, __get,
                    const Variant& name) {
  auto* data = Native::data<XMLReader>(this_);
  const xmlChar *retchar = nullptr;
  int retint = 0;

  XMLPropertyAccessor *propertyMap = xmlreader_properties_map.get(name);
  if (!propertyMap) {
    this_->raiseUndefProp(name.getStringData());
    return init_null();
  }

  if (data->m_ptr) {
    if (propertyMap->getter_char) {
      retchar = propertyMap->getter_char(data->m_ptr);
    } else if (propertyMap->getter_int) {
      retint = propertyMap->getter_int(data->m_ptr);
    }
  }

  switch (DataType(propertyMap->return_type)) {
    case KindOfBoolean:
      return retint ? true : false;
    case KindOfInt64:
      return retint;
    case KindOfString:
      if (retchar) {
        return String((char*)retchar, CopyString);
      } else {
        return empty_string_variant();
      }
    case KindOfUninit:
    case KindOfNull:
    case KindOfDouble:
    case KindOfPersistentString:
    case KindOfArray:
    case KindOfPersistentArray:
    case KindOfVec:
    case KindOfPersistentVec:
    case KindOfDict:
    case KindOfPersistentDict:
    case KindOfKeyset:
    case KindOfPersistentKeyset:
    case KindOfObject:
    case KindOfResource:
    case KindOfRef:
      return init_null();

    case KindOfClass:
      break;
  }
  not_reached();
}
Beispiel #2
0
Variant HHVM_METHOD(XMLReader, __get,
                    Variant name) {
  auto* data = Native::data<XMLReader>(this_);
  const xmlChar *retchar = nullptr;
  int retint = 0;

  XMLPropertyAccessor *propertyMap = xmlreader_properties_map.get(name);
  if (!propertyMap) {
    this_->raiseUndefProp(name.getStringData());
    return init_null();
  }

  if (data->m_ptr) {
    if (propertyMap->getter_char) {
      retchar = propertyMap->getter_char(data->m_ptr);
    } else if (propertyMap->getter_int) {
      retint = propertyMap->getter_int(data->m_ptr);
    }
  }

  switch (propertyMap->return_type) {
    case KindOfString:
      if (retchar) {
        return String((char*)retchar, CopyString);
      } else {
        return empty_string_variant();
      }
    case KindOfBoolean:
      return (retint ? true : false);

    case KindOfInt64:
      return retint;

    default:
      return init_null();
  }
  return init_null();
}
Beispiel #3
0
Variant c_XMLReader::t___get(Variant name) {
  const xmlChar *retchar = NULL;
  int retint = 0;

  XMLPropertyAccessor *propertyMap = xmlreader_properties_map.get(name);
  if (!propertyMap) {
    raiseUndefProp(name.getStringData());
    return init_null();
  }

  if (m_ptr) {
    if (propertyMap->getter_char) {
      retchar = propertyMap->getter_char(m_ptr);
    } else if (propertyMap->getter_int) {
      retint = propertyMap->getter_int(m_ptr);
    }
  }

  switch (propertyMap->return_type) {
    case KindOfString:
      if (retchar) {
        return String((char*)retchar, CopyString);
      } else {
        return empty_string_variant();
      }
    case KindOfBoolean:
      return (retint ? true : false);

    case KindOfInt64:
      return retint;

    default:
      return init_null();
  }
  return init_null();
}
Beispiel #4
0
namespace HPHP {

///////////////////////////////////////////////////////////////////////////////
// constants

const StaticString s_XMLReader("XMLReader");

const int64_t q_XMLReader$$NONE = XML_READER_TYPE_NONE;
const int64_t q_XMLReader$$ELEMENT = XML_READER_TYPE_ELEMENT;
const int64_t q_XMLReader$$ATTRIBUTE = XML_READER_TYPE_ATTRIBUTE;
const int64_t q_XMLReader$$TEXT = XML_READER_TYPE_TEXT;
const int64_t q_XMLReader$$CDATA = XML_READER_TYPE_CDATA;
const int64_t q_XMLReader$$ENTITY_REF = XML_READER_TYPE_ENTITY_REFERENCE;
const int64_t q_XMLReader$$ENTITY = XML_READER_TYPE_ENTITY;
const int64_t q_XMLReader$$PI = XML_READER_TYPE_PROCESSING_INSTRUCTION;
const int64_t q_XMLReader$$COMMENT = XML_READER_TYPE_COMMENT;
const int64_t q_XMLReader$$DOC = XML_READER_TYPE_DOCUMENT;
const int64_t q_XMLReader$$DOC_TYPE = XML_READER_TYPE_DOCUMENT_TYPE;
const int64_t q_XMLReader$$DOC_FRAGMENT = XML_READER_TYPE_DOCUMENT_FRAGMENT;
const int64_t q_XMLReader$$NOTATION = XML_READER_TYPE_NOTATION;
const int64_t q_XMLReader$$WHITESPACE = XML_READER_TYPE_WHITESPACE;
const int64_t q_XMLReader$$SIGNIFICANT_WHITESPACE =
  XML_READER_TYPE_SIGNIFICANT_WHITESPACE;
const int64_t q_XMLReader$$END_ELEMENT = XML_READER_TYPE_END_ELEMENT;
const int64_t q_XMLReader$$END_ENTITY = XML_READER_TYPE_END_ENTITY;
const int64_t q_XMLReader$$XML_DECLARATION = XML_READER_TYPE_XML_DECLARATION;
const int64_t q_XMLReader$$LOADDTD = XML_PARSER_LOADDTD;
const int64_t q_XMLReader$$DEFAULTATTRS = XML_PARSER_DEFAULTATTRS;
const int64_t q_XMLReader$$VALIDATE = XML_PARSER_VALIDATE;
const int64_t q_XMLReader$$SUBST_ENTITIES = XML_PARSER_SUBST_ENTITIES;

#define XMLREADER_LOAD_STRING 0
#define XMLREADER_LOAD_FILE 1

///////////////////////////////////////////////////////////////////////////////
// helpers

static xmlRelaxNGPtr _xmlreader_get_relaxNG(String source, int type,
    xmlRelaxNGValidityErrorFunc error_func,
    xmlRelaxNGValidityWarningFunc warn_func) {
  xmlRelaxNGParserCtxtPtr parser = nullptr;
  xmlRelaxNGPtr           sptr;
  String valid_file;

  switch (type) {
    case XMLREADER_LOAD_FILE:
      valid_file = libxml_get_valid_file_path(source.c_str());
      if (valid_file.empty()) {
        return nullptr;
      }
      parser = xmlRelaxNGNewParserCtxt(valid_file.c_str());
      break;
    case XMLREADER_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 nullptr;
  }

  if (parser == nullptr) {
    return nullptr;
  }

  if (error_func || warn_func) {
  xmlRelaxNGSetParserErrors(parser,
    (xmlRelaxNGValidityErrorFunc) error_func,
    (xmlRelaxNGValidityWarningFunc) warn_func,
    parser);
  }
  sptr = xmlRelaxNGParse(parser);
  xmlRelaxNGFreeParserCtxt(parser);

  return sptr;
}

///////////////////////////////////////////////////////////////////////////////
XMLReader::XMLReader() : m_ptr(nullptr), m_input(nullptr), m_schema(nullptr) {
}

XMLReader::~XMLReader() {
  close();
}

void XMLReader::close() {
  SYNC_VM_REGS_SCOPED();
  if (m_ptr) {
    xmlFreeTextReader(m_ptr);
    m_ptr = nullptr;
  }
  if (m_input) {
    xmlFreeParserInputBuffer(m_input);
    m_input = nullptr;
  }
  if (m_schema) {
    xmlRelaxNGFree((xmlRelaxNGPtr) m_schema);
    m_schema = nullptr;
  }
}

bool HHVM_METHOD(XMLReader, open,
                 const String& uri,
                 const Variant& encoding /*= null_variant*/,
                 int64_t options /*= 0*/) {
  auto* data = Native::data<XMLReader>(this_);
  const String& str_encoding = encoding.isNull()
                            ? null_string
                            : encoding.toString();
  SYNC_VM_REGS_SCOPED();
  if (data->m_ptr) {
    data->close();
  }

  if (uri.empty()) {
    raise_warning("Empty string supplied as input");
    return false;
  }

  String valid_file = libxml_get_valid_file_path(uri.c_str());
  xmlTextReaderPtr reader = nullptr;

  if (!valid_file.empty()) {
    // Manually create the IO context to support custom stream wrappers.
    auto stream = File::Open(valid_file, "rb");
    if (!stream->isInvalid()) {
      reader = xmlReaderForIO(libxml_streams_IO_read,
                              libxml_streams_IO_close,
                              stream.get(),
                              valid_file.data(),
                              str_encoding.data(),
                              options);
      // The xmlTextReaderPtr owns a reference to stream.
      if (reader) stream.get()->incRefCount();
    }
  }

  if (reader == nullptr) {
    raise_warning("Unable to open source data");
    return false;
  }

  data->m_ptr = reader;

  return true;
}

bool HHVM_METHOD(XMLReader, XML,
                 const String& source,
                 const Variant& encoding /*= null_variant*/,
                 int64_t options /*= 0*/) {
  auto* data = Native::data<XMLReader>(this_);
  const String& str_encoding = encoding.isNull()
                            ? null_string
                            : encoding.toString();
  xmlParserInputBufferPtr inputbfr = xmlParserInputBufferCreateMem(
    source.c_str(), source.size(), XML_CHAR_ENCODING_NONE);

  if (inputbfr != nullptr) {
    char *uri = nullptr;
    String directory = g_context->getCwd();
    if (!directory.empty()) {
      if (directory[directory.size() - 1] != '/') {
        directory += "/";
      }
      uri = (char *) xmlCanonicPath((const xmlChar *) directory.c_str());
    }

    xmlTextReaderPtr reader = xmlNewTextReader(inputbfr, uri);

    if (reader != nullptr) {
      int ret = 0;
#if LIBXML_VERSION >= 20628
      ret = xmlTextReaderSetup(reader, nullptr, uri, str_encoding.data(),
                               options);
#endif
      if (ret == 0) {
        data->m_ptr = reader;
        data->m_input = inputbfr;

        if (uri) {
          xmlFree(uri);
        }

        return true;
      }
    }

    if (uri) {
      xmlFree(uri);
    }
  }

  if (inputbfr) {
    xmlFreeParserInputBuffer(inputbfr);
  }

  raise_warning("Unable to load source data");
  return false;
}

bool HHVM_METHOD(XMLReader, close) {
  auto* data = Native::data<XMLReader>(this_);
  data->close();
  return true;
}

bool HHVM_METHOD(XMLReader, read) {
  auto* data = Native::data<XMLReader>(this_);
  SYNC_VM_REGS_SCOPED();
  if (data->m_ptr) {
    int ret = xmlTextReaderRead(data->m_ptr);
    if (ret == -1) {
      raise_warning("An Error Occurred while reading");
      return false;
    } else {
      return ret;
    }
  }
  raise_warning("Load Data before trying to read");
  return false;
}

bool HHVM_METHOD(XMLReader, next,
                 const Variant& localname /*= null_variant*/) {
  auto* data = Native::data<XMLReader>(this_);
  const String& str_localname = localname.isNull()
                              ? null_string
                              : localname.toString();
  SYNC_VM_REGS_SCOPED();
  if (data->m_ptr) {
    int ret = xmlTextReaderNext(data->m_ptr);
    while (!str_localname.empty() && ret == 1) {
      if (xmlStrEqual(xmlTextReaderConstLocalName(data->m_ptr),
          (xmlChar *)str_localname.data())) {
        return true;
      }
      ret = xmlTextReaderNext(data->m_ptr);
    }
    if (ret == -1) {
      raise_warning("An Error Occurred while reading");
      return false;
    } else {
      return ret;
    }
  }
  raise_warning("Load Data before trying to read");
  return false;
}

String XMLReader::read_string_func(xmlreader_read_char_t internal_function) {
  SYNC_VM_REGS_SCOPED();
  char *retchar = nullptr;
  if (m_ptr) {
    retchar = (char *)internal_function(m_ptr);
  }
  if (retchar) {
    String ret((const char*)retchar, CopyString);
    xmlFree(retchar);
    return ret;
  } else {
    return empty_string();
  }
}

String HHVM_METHOD(XMLReader, readString) {
  auto* data = Native::data<XMLReader>(this_);
  return data->read_string_func(xmlTextReaderReadString);
}

String HHVM_METHOD(XMLReader, readInnerXML) {
  auto* data = Native::data<XMLReader>(this_);
  return data->read_string_func(xmlTextReaderReadInnerXml);
}

String HHVM_METHOD(XMLReader, readOuterXML) {
  auto* data = Native::data<XMLReader>(this_);
  return data->read_string_func(xmlTextReaderReadOuterXml);
}

bool XMLReader::bool_func_no_arg(xmlreader_read_int_t internal_function) {
  SYNC_VM_REGS_SCOPED();
  if (m_ptr) {
    int ret = internal_function(m_ptr);
    if (ret == 1) {
      return true;
    }
  }
  return false;
}

Variant XMLReader::string_func_string_arg(String value,
    xmlreader_read_one_char_t internal_function) {
  SYNC_VM_REGS_SCOPED();

  if (value.empty()) {
    raise_warning("Argument cannot be an empty string");
    return false;
  }

  char *retchar = nullptr;
  if (m_ptr) {
    retchar = (char *)internal_function(m_ptr, (const unsigned char *)value.data());
  }

  if (retchar) {
    String ret((const char*)retchar, CopyString);
    xmlFree(retchar);
    return ret;
  } else {
    return init_null();
  }
}

Variant HHVM_METHOD(XMLReader, getAttribute,
                    const String& name) {
  auto* data = Native::data<XMLReader>(this_);
  return data->string_func_string_arg(name, xmlTextReaderGetAttribute);
}

Variant HHVM_METHOD(XMLReader, getAttributeNo,
                    int64_t index) {
  auto* data = Native::data<XMLReader>(this_);
  SYNC_VM_REGS_SCOPED();
  char *retchar = nullptr;
  if (data->m_ptr) {
    retchar = (char *)xmlTextReaderGetAttributeNo(data->m_ptr, index);
  }
  if (retchar) {
    String ret((const char*)retchar, CopyString);
    xmlFree(retchar);
    return ret;
  } else {
    return init_null();
  }
}

Variant HHVM_METHOD(XMLReader, getAttributeNs,
                    const String& name,
                    const String& namespaceURI) {
  auto* data = Native::data<XMLReader>(this_);
  SYNC_VM_REGS_SCOPED();
  if (name.empty() || namespaceURI.empty()) {
    raise_warning("Attribute Name and Namespace URI cannot be empty");
    return false;
  }

  char *retchar = nullptr;
  if (data->m_ptr) {
    retchar = (char *)xmlTextReaderGetAttributeNs(data->m_ptr,
                                                  (xmlChar *)name.data(),
                                                  (xmlChar *)namespaceURI.data());
  }

  if (retchar) {
    String ret((const char*)retchar, CopyString);
    xmlFree(retchar);
    return ret;
  } else {
    return init_null();
  }
}

bool HHVM_METHOD(XMLReader, moveToAttribute,
                 const String& name) {
  auto* data = Native::data<XMLReader>(this_);
  SYNC_VM_REGS_SCOPED();
  if (name.empty()) {
    raise_warning("Attribute Name is required");
    return false;
  }

  if (data->m_ptr) {
    int ret = xmlTextReaderMoveToAttribute(data->m_ptr, (xmlChar *)name.data());
    if (ret == 1) {
      return true;
    }
  }
  return false;
}

bool HHVM_METHOD(XMLReader, moveToAttributeNo,
                 int64_t index) {
  auto* data = Native::data<XMLReader>(this_);
  SYNC_VM_REGS_SCOPED();
  if (data->m_ptr) {
    int ret = xmlTextReaderMoveToAttributeNo(data->m_ptr, index);
    if (ret == 1) {
      return true;
    }
  }
  return false;
}

bool HHVM_METHOD(XMLReader, moveToAttributeNs,
                 const String& name,
                 const String& namespaceURI) {
  auto* data = Native::data<XMLReader>(this_);
  SYNC_VM_REGS_SCOPED();
  if (name.empty() || namespaceURI.empty()) {
    raise_warning("Attribute Name and Namespace URI cannot be empty");
    return false;
  }
  if (data->m_ptr) {
    int ret = xmlTextReaderMoveToAttributeNs(data->m_ptr,
                                             (xmlChar *)name.data(),
                                             (xmlChar *)namespaceURI.data());
    if (ret == 1) {
      return true;
    }
  }
  return false;
}

bool HHVM_METHOD(XMLReader, moveToElement) {
  auto* data = Native::data<XMLReader>(this_);
  return data->bool_func_no_arg(xmlTextReaderMoveToElement);
}

bool HHVM_METHOD(XMLReader, moveToFirstAttribute) {
  auto* data = Native::data<XMLReader>(this_);
  return data->bool_func_no_arg(xmlTextReaderMoveToFirstAttribute);
}

bool HHVM_METHOD(XMLReader, moveToNextAttribute) {
  auto* data = Native::data<XMLReader>(this_);
  return data->bool_func_no_arg(xmlTextReaderMoveToNextAttribute);
}

bool HHVM_METHOD(XMLReader, isValid) {
  auto* data = Native::data<XMLReader>(this_);
  return data->bool_func_no_arg(xmlTextReaderIsValid);
}

bool HHVM_METHOD(XMLReader, getParserProperty,
                 int64_t property) {
  auto* data = Native::data<XMLReader>(this_);
  int ret = 0;
  if (data->m_ptr) {
    ret = xmlTextReaderGetParserProp(data->m_ptr, property);
  }
  if (ret == -1) {
    raise_warning("Invalid parser property");
    return false;
  }
  return ret;
}

Variant HHVM_METHOD(XMLReader, lookupNamespace,
                    const String& prefix) {
  auto* data = Native::data<XMLReader>(this_);
  return data->string_func_string_arg(prefix, xmlTextReaderLookupNamespace);
}

bool HHVM_METHOD(XMLReader, setSchema,
                 const String& source) {
  auto* data = Native::data<XMLReader>(this_);
  SYNC_VM_REGS_SCOPED();
  if (source.empty()) {
    raise_warning("Schema data source is required");
    return false;
  }

  if (data->m_ptr) {
    int ret = xmlTextReaderSchemaValidate(data->m_ptr, source.c_str());
    if (ret == 0) {
      return true;
    }
  }
  raise_warning("Unable to set schema. This must be set prior to reading or schema contains errors.");
  return false;
}

bool HHVM_METHOD(XMLReader, setParserProperty,
                 int64_t property,
                 bool value) {
  auto* data = Native::data<XMLReader>(this_);
  if (data->m_ptr) {
    int ret = xmlTextReaderSetParserProp(data->m_ptr, property, value);
    if (ret == -1) {
      raise_warning("Invalid parser property");
      return false;
    }
    return true;
  }
  return false;
}

bool XMLReader::set_relaxng_schema(String source, int type) {
  SYNC_VM_REGS_SCOPED();
  if (source.empty()) {
    raise_warning("Schema data source is required");
    return false;
  }

  if (m_ptr) {
    int ret = -1;
    xmlRelaxNGPtr schema = nullptr;
    if (!source.empty()) {
      schema =  _xmlreader_get_relaxNG(source, type, nullptr, nullptr);
      if (schema) {
        ret = xmlTextReaderRelaxNGSetSchema(m_ptr, schema);
      }
    } else {
      ret = xmlTextReaderRelaxNGSetSchema(m_ptr, nullptr);
    }

    if (ret == 0) {
      if (m_schema) {
        xmlRelaxNGFree((xmlRelaxNGPtr) m_schema);
      }
      m_schema = schema;
      return true;
    }
  }

  raise_warning("Unable to set schema. This must be set prior to reading or schema contains errors.");
  return false;
}

bool HHVM_METHOD(XMLReader, setRelaxNGSchema,
                 const String& filename) {
  auto* data = Native::data<XMLReader>(this_);
  return data->set_relaxng_schema(filename, XMLREADER_LOAD_FILE);
}

bool HHVM_METHOD(XMLReader, setRelaxNGSchemaSource,
                 const String& source) {
  auto* data = Native::data<XMLReader>(this_);
  return data->set_relaxng_schema(source, XMLREADER_LOAD_STRING);
}

///////////////////////////////////////////////////////////////////////////////

struct XMLPropertyAccessor {
  const char *name;
  int (*getter_int)(xmlTextReaderPtr);
  const xmlChar* (*getter_char)(xmlTextReaderPtr);
  int return_type;
};

class XMLPropertyAccessorMap :
      private hphp_const_char_map<XMLPropertyAccessor*> {
public:
  explicit XMLPropertyAccessorMap(XMLPropertyAccessor* props,
                                  XMLPropertyAccessorMap *base = nullptr) {
    if (base) {
      *this = *base;
    }
    for (XMLPropertyAccessor *p = props; p->name; p++) {
      (*this)[p->name] = p;
      m_imap[p->name] = p;
    }
  }

  XMLPropertyAccessor* get(const Variant& name) {
    if (name.isString()) {
      const char* name_data = name.toString().data();
      const_iterator iter = find(name_data);
      const_iterator iiter = m_imap.find(name_data);
      if (iter != end()) {
        return iter->second;
      } else if (iiter != end()) {
        raise_warning("Accessing XMLReader::$%s with the incorrect casing",
                      name_data);
        return iiter->second;
      }
    }
    return nullptr;
  }
private:
  // Previously, this class was backed by an imap. This led to a lot of
  // code relying on accessing properties that were improperly cased.
  // Since removing this functionality could cause a lot of functionality
  // to break, instead we continue to allow access case-insensitively, but
  // with a warning
  hphp_const_char_imap<XMLPropertyAccessor*> m_imap;
};

static XMLPropertyAccessor xmlreader_properties[] = {
  { "attributeCount", xmlTextReaderAttributeCount, nullptr, KindOfInt64 },
  { "baseURI", nullptr, xmlTextReaderConstBaseUri, KindOfString },
  { "depth", xmlTextReaderDepth, nullptr, KindOfInt64 },
  { "hasAttributes", xmlTextReaderHasAttributes, nullptr, KindOfBoolean },
  { "hasValue", xmlTextReaderHasValue, nullptr, KindOfBoolean },
  { "isDefault", xmlTextReaderIsDefault, nullptr, KindOfBoolean },
  { "isEmptyElement", xmlTextReaderIsEmptyElement, nullptr, KindOfBoolean },
  { "localName", nullptr, xmlTextReaderConstLocalName, KindOfString },
  { "name", nullptr, xmlTextReaderConstName, KindOfString },
  { "namespaceURI", nullptr, xmlTextReaderConstNamespaceUri, KindOfString },
  { "nodeType", xmlTextReaderNodeType, nullptr, KindOfInt64 },
  { "prefix", nullptr, xmlTextReaderConstPrefix, KindOfString },
  { "value", nullptr, xmlTextReaderConstValue, KindOfString },
  { "xmlLang", nullptr, xmlTextReaderConstXmlLang, KindOfString },
  { nullptr, nullptr, nullptr }
};

static XMLPropertyAccessorMap xmlreader_properties_map
((XMLPropertyAccessor*)xmlreader_properties);

Variant HHVM_METHOD(XMLReader, __get,
                    Variant name) {
  auto* data = Native::data<XMLReader>(this_);
  const xmlChar *retchar = nullptr;
  int retint = 0;

  XMLPropertyAccessor *propertyMap = xmlreader_properties_map.get(name);
  if (!propertyMap) {
    this_->raiseUndefProp(name.getStringData());
    return init_null();
  }

  if (data->m_ptr) {
    if (propertyMap->getter_char) {
      retchar = propertyMap->getter_char(data->m_ptr);
    } else if (propertyMap->getter_int) {
      retint = propertyMap->getter_int(data->m_ptr);
    }
  }

  switch (propertyMap->return_type) {
    case KindOfBoolean:
      return retint ? true : false;
    case KindOfInt64:
      return retint;
    case KindOfString:
      if (retchar) {
        return String((char*)retchar, CopyString);
      } else {
        return empty_string_variant();
      }
    case KindOfUninit:
    case KindOfNull:
    case KindOfDouble:
    case KindOfStaticString:
    case KindOfArray:
    case KindOfObject:
    case KindOfResource:
    case KindOfRef:
      return init_null();

    case KindOfClass:
      break;
  }
  not_reached();
}

Variant HHVM_METHOD(XMLReader, expand,
                    const Variant& basenode /* = null */) {
  auto* data = Native::data<XMLReader>(this_);
  Object doc;
  xmlDocPtr docp = nullptr;
  SYNC_VM_REGS_SCOPED();

  if (!basenode.isNull()) {
    DOMNode *dombasenode = toDOMNode(basenode.toObject().get());
    doc = dombasenode->doc();
    docp = (xmlDocPtr) toDOMNode(doc.get())->m_node;
    if (docp == nullptr) {
      raise_warning("Invalid State Error");
      return false;
    }
  } else {
    doc = DOMDocument::newInstance();
  }

  if (data->m_ptr) {
    xmlNodePtr node = xmlTextReaderExpand(data->m_ptr);
    if (node == nullptr) {
      raise_warning("An Error Occurred while expanding");
      return false;
    } else {
      xmlNodePtr nodec = xmlDocCopyNode(node, docp, 1);
      if (nodec == nullptr) {
        raise_notice("Cannot expand this node type");
        return false;
      } else {
        return php_dom_create_object(nodec, doc, false);
      }
    }
  }

  raise_warning("Load Data before trying to read");
  return false;
}

///////////////////////////////////////////////////////////////////////////////

#define REGISTER_XML_READER_CONSTANT(name)                                     \
  Native::registerClassConstant<KindOfInt64>(s_XMLReader.get(),                \
                                             makeStaticString(#name),          \
                                             q_XMLReader$$##name)              \

static class XMLReaderExtension final : public Extension {
public:
  XMLReaderExtension() : Extension("xmlreader", "0.1") {}
  void moduleInit() override {
    REGISTER_XML_READER_CONSTANT(NONE);
    REGISTER_XML_READER_CONSTANT(ELEMENT);
    REGISTER_XML_READER_CONSTANT(ATTRIBUTE);
    REGISTER_XML_READER_CONSTANT(TEXT);
    REGISTER_XML_READER_CONSTANT(CDATA);
    REGISTER_XML_READER_CONSTANT(ENTITY_REF);
    REGISTER_XML_READER_CONSTANT(ENTITY);
    REGISTER_XML_READER_CONSTANT(PI);
    REGISTER_XML_READER_CONSTANT(COMMENT);
    REGISTER_XML_READER_CONSTANT(DOC);
    REGISTER_XML_READER_CONSTANT(DOC_TYPE);
    REGISTER_XML_READER_CONSTANT(DOC_FRAGMENT);
    REGISTER_XML_READER_CONSTANT(NOTATION);
    REGISTER_XML_READER_CONSTANT(WHITESPACE);
    REGISTER_XML_READER_CONSTANT(SIGNIFICANT_WHITESPACE);
    REGISTER_XML_READER_CONSTANT(END_ELEMENT);
    REGISTER_XML_READER_CONSTANT(END_ENTITY);
    REGISTER_XML_READER_CONSTANT(XML_DECLARATION);
    REGISTER_XML_READER_CONSTANT(LOADDTD);
    REGISTER_XML_READER_CONSTANT(DEFAULTATTRS);
    REGISTER_XML_READER_CONSTANT(VALIDATE);
    REGISTER_XML_READER_CONSTANT(SUBST_ENTITIES);

    HHVM_ME(XMLReader, open);
    HHVM_ME(XMLReader, XML);
    HHVM_ME(XMLReader, close);
    HHVM_ME(XMLReader, read);
    HHVM_ME(XMLReader, next);
    HHVM_ME(XMLReader, readString);
    HHVM_ME(XMLReader, readInnerXML);
    HHVM_ME(XMLReader, readOuterXML);
    HHVM_ME(XMLReader, moveToNextAttribute);
    HHVM_ME(XMLReader, getAttribute);
    HHVM_ME(XMLReader, getAttributeNo);
    HHVM_ME(XMLReader, getAttributeNs);
    HHVM_ME(XMLReader, moveToAttribute);
    HHVM_ME(XMLReader, moveToAttributeNo);
    HHVM_ME(XMLReader, moveToAttributeNs);
    HHVM_ME(XMLReader, moveToElement);
    HHVM_ME(XMLReader, moveToFirstAttribute);
    HHVM_ME(XMLReader, isValid);
    HHVM_ME(XMLReader, __get);
    HHVM_ME(XMLReader, getParserProperty);
    HHVM_ME(XMLReader, lookupNamespace);
    HHVM_ME(XMLReader, setSchema);
    HHVM_ME(XMLReader, setParserProperty);
    HHVM_ME(XMLReader, setRelaxNGSchema);
    HHVM_ME(XMLReader, setRelaxNGSchemaSource);
    HHVM_ME(XMLReader, expand);

    Native::registerNativeDataInfo<XMLReader>(s_XMLReader.get());

    loadSystemlib();
  }
} s_xml_reader_extension;

///////////////////////////////////////////////////////////////////////////////
}
Beispiel #5
0
namespace HPHP {

IMPLEMENT_DEFAULT_EXTENSION_VERSION(xmlreader, 0.1);

///////////////////////////////////////////////////////////////////////////////
// constants
const int64_t q_XMLReader$$NONE = XML_READER_TYPE_NONE;
const int64_t q_XMLReader$$ELEMENT = XML_READER_TYPE_ELEMENT;
const int64_t q_XMLReader$$ATTRIBUTE = XML_READER_TYPE_ATTRIBUTE;
const int64_t q_XMLReader$$TEXT = XML_READER_TYPE_TEXT;
const int64_t q_XMLReader$$CDATA = XML_READER_TYPE_CDATA;
const int64_t q_XMLReader$$ENTITY_REF = XML_READER_TYPE_ENTITY_REFERENCE;
const int64_t q_XMLReader$$ENTITY = XML_READER_TYPE_ENTITY;
const int64_t q_XMLReader$$PI = XML_READER_TYPE_PROCESSING_INSTRUCTION;
const int64_t q_XMLReader$$COMMENT = XML_READER_TYPE_COMMENT;
const int64_t q_XMLReader$$DOC = XML_READER_TYPE_DOCUMENT;
const int64_t q_XMLReader$$DOC_TYPE = XML_READER_TYPE_DOCUMENT_TYPE;
const int64_t q_XMLReader$$DOC_FRAGMENT = XML_READER_TYPE_DOCUMENT_FRAGMENT;
const int64_t q_XMLReader$$NOTATION = XML_READER_TYPE_NOTATION;
const int64_t q_XMLReader$$WHITESPACE = XML_READER_TYPE_WHITESPACE;
const int64_t q_XMLReader$$SIGNIFICANT_WHITESPACE = XML_READER_TYPE_SIGNIFICANT_WHITESPACE;
const int64_t q_XMLReader$$END_ELEMENT = XML_READER_TYPE_END_ELEMENT;
const int64_t q_XMLReader$$END_ENTITY = XML_READER_TYPE_END_ENTITY;
const int64_t q_XMLReader$$XML_DECLARATION = XML_READER_TYPE_XML_DECLARATION;
const int64_t q_XMLReader$$LOADDTD = XML_PARSER_LOADDTD;
const int64_t q_XMLReader$$DEFAULTATTRS = XML_PARSER_DEFAULTATTRS;
const int64_t q_XMLReader$$VALIDATE = XML_PARSER_VALIDATE;
const int64_t q_XMLReader$$SUBST_ENTITIES = XML_PARSER_SUBST_ENTITIES;

#define XMLREADER_LOAD_STRING 0
#define XMLREADER_LOAD_FILE 1

///////////////////////////////////////////////////////////////////////////////
// helpers

static String _xmlreader_get_valid_file_path(const char *source) {
  int isFileUri = 0;

  xmlURI *uri = xmlCreateURI();
  xmlChar *escsource = xmlURIEscapeStr((xmlChar*)source, (xmlChar*)":");
  xmlParseURIReference(uri, (char*)escsource);
  xmlFree(escsource);

  if (uri->scheme != NULL) {
    /* absolute file uris - libxml only supports localhost or empty host */
    if (strncasecmp(source, "file:///",8) == 0) {
      isFileUri = 1;
      source += 7;
    } else if (strncasecmp(source, "file://localhost/",17) == 0) {
      isFileUri = 1;
      source += 16;
    }
  }

  String file_dest = String(source, CopyString);
  if ((uri->scheme == NULL || isFileUri)) {
    file_dest = File::TranslatePath(file_dest);
  }
  xmlFreeURI(uri);
  return file_dest;
}

static xmlRelaxNGPtr _xmlreader_get_relaxNG(String source, int type,
                                            xmlRelaxNGValidityErrorFunc error_func,
                                            xmlRelaxNGValidityWarningFunc warn_func )
{
  xmlRelaxNGParserCtxtPtr parser = NULL;
  xmlRelaxNGPtr           sptr;
  String valid_file;

  switch (type) {
    case XMLREADER_LOAD_FILE:
      valid_file = _xmlreader_get_valid_file_path(source.c_str());
      if (valid_file.empty()) {
        return NULL;
      }
      parser = xmlRelaxNGNewParserCtxt(valid_file.c_str());
      break;
    case XMLREADER_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 NULL;
  }

  if (parser == NULL) {
    return NULL;
  }

  if (error_func || warn_func) {
  xmlRelaxNGSetParserErrors(parser,
    (xmlRelaxNGValidityErrorFunc) error_func,
    (xmlRelaxNGValidityWarningFunc) warn_func,
    parser);
  }
  sptr = xmlRelaxNGParse(parser);
  xmlRelaxNGFreeParserCtxt(parser);

  return sptr;
}

///////////////////////////////////////////////////////////////////////////////
c_XMLReader::c_XMLReader(Class* cb) :
    ExtObjectDataFlags<ObjectData::UseGet>(cb), m_ptr(NULL), m_input(NULL), m_schema(NULL) {
}

c_XMLReader::~c_XMLReader() {
  close_impl();
}

void c_XMLReader::t___construct() {
}

bool c_XMLReader::t_open(const String& uri, const String& encoding /*= null_string*/, int64_t options /*= 0*/) {
  if (m_ptr) {
    t_close();
  }

  if (uri.empty()) {
    raise_warning("Empty string supplied as input");
    return false;
  }

  String valid_file = _xmlreader_get_valid_file_path(uri.c_str());
  xmlTextReaderPtr reader = NULL;

  if (!valid_file.empty()) {
    reader = xmlReaderForFile(valid_file.data(), encoding.data(), options);
  }

  if (reader == NULL) {
    raise_warning("Unable to open source data");
    return false;
  }

  m_ptr = reader;

  return true;
}

bool c_XMLReader::t_xml(const String& source, const String& encoding /*= null_string*/, int64_t options /*= 0*/) {
  xmlParserInputBufferPtr inputbfr = xmlParserInputBufferCreateMem(source.c_str(), source.size(), XML_CHAR_ENCODING_NONE);

  if (inputbfr != NULL) {
    char *uri = NULL;
    String directory = g_context->getCwd();
    if (!directory.empty()) {
      if (directory[directory.size() - 1] != '/') {
        directory += "/";
      }
      uri = (char *) xmlCanonicPath((const xmlChar *) directory.c_str());
    }

    xmlTextReaderPtr reader = xmlNewTextReader(inputbfr, uri);

    if (reader != NULL) {
      int ret = 0;
#if LIBXML_VERSION >= 20628
      ret = xmlTextReaderSetup(reader, NULL, uri, encoding.data(), options);
#endif
      if (ret == 0) {
        m_ptr = reader;
        m_input = inputbfr;

        if (uri) {
          xmlFree(uri);
        }

        return true;
      }
    }

    if (uri) {
      xmlFree(uri);
    }
  }

  if (inputbfr) {
    xmlFreeParserInputBuffer(inputbfr);
  }

  raise_warning("Unable to load source data");
  return false;
}

void c_XMLReader::close_impl() {
  if (m_ptr) {
    xmlFreeTextReader(m_ptr);
    m_ptr = NULL;
  }
  if (m_input) {
    xmlFreeParserInputBuffer(m_input);
    m_input = NULL;
  }
  if (m_schema) {
    xmlRelaxNGFree((xmlRelaxNGPtr) m_schema);
    m_schema = NULL;
  }
}

bool c_XMLReader::t_close() {
  close_impl();
  return true;
}

bool c_XMLReader::t_read() {
  if (m_ptr) {
    int ret = xmlTextReaderRead(m_ptr);
    if (ret == -1) {
      raise_warning("An Error Occured while reading");
      return false;
    } else {
      return ret;
    }
  }
  raise_warning("Load Data before trying to read");
  return false;
}

bool c_XMLReader::t_next(const String& localname /*= null_string*/) {
  if (m_ptr) {
    int ret = xmlTextReaderNext(m_ptr);
    while (!localname.empty() && ret == 1) {
      if (xmlStrEqual(xmlTextReaderConstLocalName(m_ptr), (xmlChar *)localname.data())) {
        return true;
      }
      ret = xmlTextReaderNext(m_ptr);
    }
    if (ret == -1) {
      raise_warning("An Error Occured while reading");
      return false;
    } else {
      return ret;
    }
  }
  raise_warning("Load Data before trying to read");
  return false;
}

String c_XMLReader::read_string_func(xmlreader_read_char_t internal_function) {
  char *retchar = NULL;
  if (m_ptr) {
    retchar = (char *)internal_function(m_ptr);
  }
  if (retchar) {
    String ret((const char*)retchar, CopyString);
    xmlFree(retchar);
    return ret;
  } else {
    return empty_string();
  }
}

String c_XMLReader::t_readstring() {
  return read_string_func(xmlTextReaderReadString);
}

String c_XMLReader::t_readinnerxml() {
  return read_string_func(xmlTextReaderReadInnerXml);
}

String c_XMLReader::t_readouterxml() {
  return read_string_func(xmlTextReaderReadOuterXml);
}

bool c_XMLReader::bool_func_no_arg(xmlreader_read_int_t internal_function) {
  if (m_ptr) {
    int ret = internal_function(m_ptr);
    if (ret == 1) {
      return true;
    }
  }
  return false;
}

Variant c_XMLReader::string_func_string_arg(String value, xmlreader_read_one_char_t internal_function) {

  if (value.empty()) {
    raise_warning("Argument cannot be an empty string");
    return false;
  }

  char *retchar = NULL;
  if (m_ptr) {
    retchar = (char *)internal_function(m_ptr, (const unsigned char *)value.data());
  }

  if (retchar) {
    String ret((const char*)retchar, CopyString);
    xmlFree(retchar);
    return ret;
  } else {
    return init_null();
  }
}

Variant c_XMLReader::t_getattribute(const String& name) {
  return string_func_string_arg(name, xmlTextReaderGetAttribute);
}

Variant c_XMLReader::t_getattributeno(int64_t index) {
  char *retchar = NULL;
  if (m_ptr) {
    retchar = (char *)xmlTextReaderGetAttributeNo(m_ptr, index);
  }
  if (retchar) {
    String ret((const char*)retchar, CopyString);
    xmlFree(retchar);
    return ret;
  } else {
    return init_null();
  }
}

Variant c_XMLReader::t_getattributens(const String& name, const String& namespaceURI) {
  if (name.empty() || namespaceURI.empty()) {
    raise_warning("Attribute Name and Namespace URI cannot be empty");
    return false;
  }

  char *retchar = NULL;
  if (m_ptr) {
    retchar = (char *)xmlTextReaderGetAttributeNs(m_ptr,
                                                  (xmlChar *)name.data(),
                                                  (xmlChar *)namespaceURI.data());
  }

  if (retchar) {
    String ret((const char*)retchar, CopyString);
    xmlFree(retchar);
    return ret;
  } else {
    return init_null();
  }
}

bool c_XMLReader::t_movetoattribute(const String& name) {
  if (name.empty()) {
    raise_warning("Attribute Name is required");
    return false;
  }

  if (m_ptr) {
    int ret = xmlTextReaderMoveToAttribute(m_ptr, (xmlChar *)name.data());
    if (ret == 1) {
      return true;
    }
  }
  return false;
}

bool c_XMLReader::t_movetoattributeno(int64_t index) {
  if (m_ptr) {
    int ret = xmlTextReaderMoveToAttributeNo(m_ptr, index);
    if (ret == 1) {
      return true;
    }
  }
  return false;
}

bool c_XMLReader::t_movetoattributens(const String& name, const String& namespaceURI) {
  if (name.empty() || namespaceURI.empty()) {
    raise_warning("Attribute Name and Namespace URI cannot be empty");
    return false;
  }
  if (m_ptr) {
    int ret = xmlTextReaderMoveToAttributeNs(m_ptr,
                                             (xmlChar *)name.data(),
                                             (xmlChar *)namespaceURI.data());
    if (ret == 1) {
      return true;
    }
  }
  return false;
}

bool c_XMLReader::t_movetoelement() {
  return bool_func_no_arg(xmlTextReaderMoveToElement);
}

bool c_XMLReader::t_movetofirstattribute() {
  return bool_func_no_arg(xmlTextReaderMoveToFirstAttribute);
}

bool c_XMLReader::t_movetonextattribute() {
  return bool_func_no_arg(xmlTextReaderMoveToNextAttribute);
}

bool c_XMLReader::t_isvalid() {
  return bool_func_no_arg(xmlTextReaderIsValid);
}

bool c_XMLReader::t_getparserproperty(int64_t property) {
  int ret = 0;
  if (m_ptr) {
    ret = xmlTextReaderGetParserProp(m_ptr, property);
  }
  if (ret == -1) {
    raise_warning("Invalid parser property");
    return false;
  }
  return ret;
}

Variant c_XMLReader::t_lookupnamespace(const String& prefix) {
  return string_func_string_arg(prefix, xmlTextReaderLookupNamespace);
}

bool c_XMLReader::t_setschema(const String& source) {
  if (source.empty()) {
    raise_warning("Schema data source is required");
    return false;
  }

  if (m_ptr) {
    int ret = xmlTextReaderSchemaValidate(m_ptr, source.c_str());
    if (ret == 0) {
      return true;
    }
  }
  raise_warning("Unable to set schema. This must be set prior to reading or schema contains errors.");
  return false;
}

bool c_XMLReader::t_setparserproperty(int64_t property, bool value) {
  if (m_ptr) {
    int ret = xmlTextReaderSetParserProp(m_ptr, property, value);
    if (ret == -1) {
      raise_warning("Invalid parser property");
      return false;
    }
    return true;
  }
  return false;
}

bool c_XMLReader::set_relaxng_schema(String source, int type) {
  if (source.empty()) {
    raise_warning("Schema data source is required");
    return false;
  }

  if (m_ptr) {
    int ret = -1;
    xmlRelaxNGPtr schema = NULL;
    if (!source.empty()) {
      schema =  _xmlreader_get_relaxNG(source, type, NULL, NULL);
      if (schema) {
        ret = xmlTextReaderRelaxNGSetSchema(m_ptr, schema);
      }
    } else {
      ret = xmlTextReaderRelaxNGSetSchema(m_ptr, NULL);
    }

    if (ret == 0) {
      if (m_schema) {
        xmlRelaxNGFree((xmlRelaxNGPtr) m_schema);
      }
      m_schema = schema;
      return true;
    }
  }

  raise_warning("Unable to set schema. This must be set prior to reading or schema contains errors.");
  return false;
}

bool c_XMLReader::t_setrelaxngschema(const String& filename) {
  return set_relaxng_schema(filename, XMLREADER_LOAD_FILE);
}

bool c_XMLReader::t_setrelaxngschemasource(const String& source) {
  return set_relaxng_schema(source, XMLREADER_LOAD_STRING);
}

///////////////////////////////////////////////////////////////////////////////

struct XMLPropertyAccessor {
  const char *name;
  int (*getter_int)(xmlTextReaderPtr);
  const xmlChar* (*getter_char)(xmlTextReaderPtr);
  int return_type;
};

class XMLPropertyAccessorMap :
      private hphp_const_char_map<XMLPropertyAccessor*> {
public:
  explicit XMLPropertyAccessorMap(XMLPropertyAccessor* props,
                                  XMLPropertyAccessorMap *base = nullptr) {
    if (base) {
      *this = *base;
    }
    for (XMLPropertyAccessor *p = props; p->name; p++) {
      (*this)[p->name] = p;
      m_imap[p->name] = p;
    }
  }

  XMLPropertyAccessor* get(const Variant& name) {
    if (name.isString()) {
      const char* name_data = name.toString().data();
      const_iterator iter = find(name_data);
      const_iterator iiter = m_imap.find(name_data);
      if (iter != end()) {
        return iter->second;
      } else if (iiter != end()) {
        raise_warning("Accessing XMLReader::$%s with the incorrect casing",
                      name_data);
        return iiter->second;
      }
    }
    return NULL;
  }
private:
  // Previously, this class was backed by an imap. This led to a lot of
  // code relying on accessing properties that were improperly cased.
  // Since removing this functionality could cause a lot of functionality
  // to break, instead we continue to allow access case-insensitively, but
  // with a warning
  hphp_const_char_imap<XMLPropertyAccessor*> m_imap;
};

static XMLPropertyAccessor xmlreader_properties[] = {
  { "attributeCount", xmlTextReaderAttributeCount, NULL, KindOfInt64 },
  { "baseURI", NULL, xmlTextReaderConstBaseUri, KindOfString },
  { "depth", xmlTextReaderDepth, NULL, KindOfInt64 },
  { "hasAttributes", xmlTextReaderHasAttributes, NULL, KindOfBoolean },
  { "hasValue", xmlTextReaderHasValue, NULL, KindOfBoolean },
  { "isDefault", xmlTextReaderIsDefault, NULL, KindOfBoolean },
  { "isEmptyElement", xmlTextReaderIsEmptyElement, NULL, KindOfBoolean },
  { "localName", NULL, xmlTextReaderConstLocalName, KindOfString },
  { "name", NULL, xmlTextReaderConstName, KindOfString },
  { "namespaceURI", NULL, xmlTextReaderConstNamespaceUri, KindOfString },
  { "nodeType", xmlTextReaderNodeType, NULL, KindOfInt64 },
  { "prefix", NULL, xmlTextReaderConstPrefix, KindOfString },
  { "value", NULL, xmlTextReaderConstValue, KindOfString },
  { "xmlLang", NULL, xmlTextReaderConstXmlLang, KindOfString },
  { NULL, NULL, NULL }
};

static XMLPropertyAccessorMap xmlreader_properties_map
((XMLPropertyAccessor*)xmlreader_properties);

Variant c_XMLReader::t___get(Variant name) {
  const xmlChar *retchar = NULL;
  int retint = 0;

  XMLPropertyAccessor *propertyMap = xmlreader_properties_map.get(name);
  if (!propertyMap) {
    raiseUndefProp(name.getStringData());
    return init_null();
  }

  if (m_ptr) {
    if (propertyMap->getter_char) {
      retchar = propertyMap->getter_char(m_ptr);
    } else if (propertyMap->getter_int) {
      retint = propertyMap->getter_int(m_ptr);
    }
  }

  switch (propertyMap->return_type) {
    case KindOfString:
      if (retchar) {
        return String((char*)retchar, CopyString);
      } else {
        return empty_string_variant();
      }
    case KindOfBoolean:
      return (retint ? true : false);

    case KindOfInt64:
      return retint;

    default:
      return init_null();
  }
  return init_null();
}

Variant c_XMLReader::t_expand(const Object& basenode /* = null */) {
  p_DOMDocument doc;
  xmlDocPtr docp = nullptr;

  if (!basenode.isNull()) {
    c_DOMNode *dombasenode = basenode.getTyped<c_DOMNode>();
    doc = dombasenode->doc();
    docp = (xmlDocPtr) doc->m_node;
    if (docp == nullptr) {
      raise_warning("Invalid State Error");
      return false;
    }
  } else {
    doc = (p_DOMDocument) SystemLib::AllocDOMDocumentObject();
  }

  if (m_ptr) {
    xmlNodePtr node = xmlTextReaderExpand(m_ptr);
    if (node == nullptr) {
      raise_warning("An Error Occurred while expanding");
      return false;
    } else {
      xmlNodePtr nodec = xmlDocCopyNode(node, docp, 1);
      if (nodec == nullptr) {
        raise_notice("Cannot expand this node type");
        return false;
      } else {
        return php_dom_create_object(nodec, doc, false);
      }
    }
  }

  raise_warning("Load Data before trying to read");
  return false;
}

Variant c_XMLReader::t___destruct() {
  return init_null();
}

///////////////////////////////////////////////////////////////////////////////
}