X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=php5-swig%2Fxml-template.swig;h=458296ef994e6d782a6f854854d57c921816298d;hb=7d1da52761b9ec9747c4400609658a379a34392c;hp=d395ac20a72cc097ed7335b8dde68b7f6af1776a;hpb=b6bae3e8cf33edf6bbedcf6d3266e581d06507af;p=xml-template diff --git a/php5-swig/xml-template.swig b/php5-swig/xml-template.swig index d395ac2..458296e 100644 --- a/php5-swig/xml-template.swig +++ b/php5-swig/xml-template.swig @@ -1,9 +1,23 @@ %module XML_Template_SWIG +%include + +struct XmlDocPtrWrapper { + ~XmlDocPtrWrapper(); +}; %{ +#include +#include + #include "../c++11/xml-template.h" - + +struct XmlDocWrapper { + ~XmlDocWrapper() { xmlFreeDoc(ptr); } + xmlDocPtr ptr; +}; +typedef std::shared_ptr XmlDocPtrWrapper; + bool is_associative_array(HashTable *ht) { if (ht->nNumOfElements == 0) { @@ -44,7 +58,13 @@ Directive* convert_php_objects_to_directive(zval *obj) } return new Substitute(my_map); } else { - printf("ARRAY\n"); + std::vector subdirectives; + for (unsigned i = 0; i < ht->nNumOfElements; ++i) { + zval **data; + zend_hash_index_find(ht, i, (void **)&data); + subdirectives.push_back(convert_php_objects_to_directive(*data)); + } + return new Clone(subdirectives); } break; } @@ -52,6 +72,25 @@ Directive* convert_php_objects_to_directive(zval *obj) char *str = Z_STRVAL_P(obj); return new Replace(str); } + case IS_LONG: { + char str[256]; + snprintf(str, sizeof(str), "%ld", Z_LVAL_P(obj)); + return new Replace(str); + } + case IS_DOUBLE: { + char str[256]; + snprintf(str, sizeof(str), "%f", Z_DVAL_P(obj)); + return new Replace(str); + } + case IS_RESOURCE: { + XmlDocPtrWrapper *doc; + if (SWIG_ConvertPtr(obj, (void **)&doc, SWIGTYPE_p_XmlDocPtrWrapper, 0) < 0 || doc == NULL) { + return NULL; + } + return new ReplaceInclude(xmlCopyDoc((*doc)->ptr, 1)); + } + case IS_NULL: + return new Replace { "" }; default: printf("WARNING: Unknown type %d!\n", Z_TYPE_P(obj)); break; @@ -60,12 +99,155 @@ Directive* convert_php_objects_to_directive(zval *obj) return NULL; } +XmlDocPtrWrapper XML_Template_process_file(const std::string &input_filename, Directive *root_directive, bool clean) +{ + xmlDocPtr ret = process_file(input_filename, root_directive, clean); + delete root_directive; + return XmlDocPtrWrapper(new XmlDocWrapper { ret }); +} + +void XML_Template_process(XmlDocPtrWrapper doc, Directive *root_directive, bool clean) +{ + root_directive->process(xmlDocGetRootElement(doc->ptr), clean); + delete root_directive; +} + +namespace { + +int write_to_string(void *context, const char *buffer, int len) +{ + std::string *str = reinterpret_cast(context); + str->append(buffer, len); + return len; +} + +int close_string(void *context) +{ + return 0; +} + +} // namespace + +std::string XML_Template_convert_doc_to_string(XmlDocPtrWrapper doc, bool prettyprint) +{ + xmlIndentTreeOutput = prettyprint; + std::string ret; + xmlOutputBufferPtr buf = xmlOutputBufferCreateIO(write_to_string, close_string, &ret, NULL); + xmlSaveFormatFileTo(buf, doc->ptr, "UTF-8", prettyprint); + return ret; +} + +namespace { + +// Remove document fragments (ie. move their content up in the parent node) +// and combine neighboring text nodes into one. +void normalize_node(xmlNodePtr node) +{ + xmlNode *next_child; + for (xmlNode *child = node->children; child != NULL; child = next_child) { + next_child = child->next; + if (child->type == XML_DOCUMENT_FRAG_NODE) { + while (child->children != NULL) { + xmlAddPrevSibling(child, child->children); + } + + xmlUnlinkNode(child); + xmlFreeNode(child); + } + } + + // xmlAddPrevSibling merges adjacent text nodes, but many other things + // (including xmlUnlinkNode) do not, so make an extra pass. + for (xmlNode *child = node->children; child != NULL; child = child->next) { + while (child->type == XML_TEXT_NODE && (child->next != NULL && child->next->type == XML_TEXT_NODE)) { + xmlNode *next_child = child->next; + + xmlChar *content = xmlNodeGetContent(next_child); + xmlNodeAddContent(child, content); + xmlFree(content); + + xmlUnlinkNode(next_child); + xmlFreeNode(next_child); + } + normalize_node(child); + } +} + +// Clean the page of non-necessary whitespace. Leaves whitespace alone if and +// only if xml:space="preserve" on the element. (IOW, it doesn't parse the DTDs, +// nor the CSS.) +void clean_node(xmlNodePtr node, bool preserve_whitespace, bool aggressive) +{ + if (node->type == XML_TEXT_NODE) { + std::string content = reinterpret_cast(xmlNodeGetContent(node)); + if (!preserve_whitespace) { + unsigned dstpos = 0; + for (unsigned srcpos = 0; srcpos < content.size(); ++srcpos, ++dstpos) { + if (content[srcpos] == '\n' || + content[srcpos] == '\t' || + content[srcpos] == ' ') { + content[dstpos] = ' '; + + // compress double spaces + if (dstpos > 0 && content[dstpos - 1] == ' ') { + --dstpos; + } + } else { + content[dstpos] = content[srcpos]; + } + } + content.resize(dstpos); + } + if (content.empty() || (aggressive && content == " ")) { + xmlUnlinkNode(node); + xmlFreeNode(node); + } else { + xmlNodeSetContentLen(node, reinterpret_cast(content.data()), content.size()); + } + } else { + if (node->type == XML_ELEMENT_NODE) { + xmlChar *space = xmlGetProp(node, reinterpret_cast("xml:space")); + preserve_whitespace = (space != NULL && strcmp(reinterpret_cast(space), "preserve") == 0); + } + + xmlNode *next_child; + for (xmlNode *child = node->children; child != NULL; child = next_child) { + next_child = child->next; + clean_node(child, preserve_whitespace, aggressive); + } + + if (node->type == XML_ELEMENT_NODE && node->children == NULL) { + std::string tag = reinterpret_cast(node->name); + + // These are the only elements allowed in XHTML to be EMPTY, + // so insert dummy nodes to prevent the output from using + // the syntax where not appropriate. + if (tag != "base" && tag != "meta" && tag != "link" && tag != "hr" && + tag != "br" && tag != "param" && tag != "img" && tag != "area" && + tag != "input" && tag != "col") { + xmlNode *text = xmlNewText(reinterpret_cast("")); + xmlAddChild(node, text); + } + } + } +} + +} // namespace + +void XML_Template_clean_whitespace(XmlDocPtrWrapper doc, bool aggressive) +{ + normalize_node(xmlDocGetRootElement(doc->ptr)); + clean_node(xmlDocGetRootElement(doc->ptr), false, aggressive); +} + %} %typemap(in) Directive* { $1 = convert_php_objects_to_directive(*$input); } -xmlDocPtr process_file(const char *input_filename, Directive *root_directive, bool clean); -void output_to_fd_and_free(xmlDocPtr doc, int fd); +XmlDocPtrWrapper XML_Template_process_file(const std::string &input_filename, Directive *root_directive, bool clean); +void XML_Template_process(XmlDocPtrWrapper doc, Directive *root_directive, bool clean); +void XML_Template_clean_whitespace(XmlDocPtrWrapper doc, bool aggressive); +std::string XML_Template_convert_doc_to_string(XmlDocPtrWrapper doc, bool prettyprint);