]> git.sesse.net Git - xml-template/blobdiff - php5-swig/xml-template.swig
Fix a segfaulting bug in the PHP5 SWIG version; we would get confused when PHP had...
[xml-template] / php5-swig / xml-template.swig
index 225f8bbd2e2d9a4c17df0d3937bef0721a15beff..458296ef994e6d782a6f854854d57c921816298d 100644 (file)
@@ -1,4 +1,5 @@
 %module XML_Template_SWIG
+%include <std_string.i>
 
 struct XmlDocPtrWrapper {
        ~XmlDocPtrWrapper();
@@ -7,6 +8,7 @@ struct XmlDocPtrWrapper {
 %{
 
 #include <memory>
+#include <libxml/globals.h>
 
 #include "../c++11/xml-template.h"
 
@@ -70,15 +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->get()->ptr, 1));
+               return new ReplaceInclude(xmlCopyDoc((*doc)->ptr, 1));
        }
        case IS_NULL:
-               return NULL;
+               return new Replace { "" };
        default:
                printf("WARNING: Unknown type %d!\n", Z_TYPE_P(obj));
                break;
@@ -87,17 +99,145 @@ Directive* convert_php_objects_to_directive(zval *obj)
        return NULL;
 }
 
-XmlDocPtrWrapper XML_Template_process_file(const char *input_filename, Directive *root_directive, bool clean)
+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 output_to_fd(XmlDocPtrWrapper doc, int fd)
+
+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<std::string *>(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<const char *>(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<const xmlChar *>(content.data()), content.size());
+               }
+       } else {
+               if (node->type == XML_ELEMENT_NODE) {
+                       xmlChar *space = xmlGetProp(node, reinterpret_cast<const xmlChar *>("xml:space"));
+                       preserve_whitespace = (space != NULL && strcmp(reinterpret_cast<const char *>(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<const char *>(node->name);
+
+                       // These are the only elements allowed in XHTML to be EMPTY,
+                       // so insert dummy nodes to prevent the output from using
+                       // the <foo/> 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<const xmlChar *>(""));
+                               xmlAddChild(node, text);
+                       }
+               }
+       }
+}
+
+}  // namespace
+
+void XML_Template_clean_whitespace(XmlDocPtrWrapper doc, bool aggressive)
 {
-       xmlOutputBufferPtr buf = xmlOutputBufferCreateFd(fd, NULL);
-       xmlSaveFileTo(buf, doc->ptr, NULL);
+       normalize_node(xmlDocGetRootElement(doc->ptr));
+       clean_node(xmlDocGetRootElement(doc->ptr), false, aggressive);
 }
 
 %}
@@ -106,6 +246,8 @@ void output_to_fd(XmlDocPtrWrapper doc, int fd)
        $1 = convert_php_objects_to_directive(*$input);
 }
 
-XmlDocPtrWrapper XML_Template_process_file(const char *input_filename, Directive *root_directive, bool clean);
-void output_to_fd(XmlDocPtrWrapper 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);