1 %module XML_Template_SWIG
2 %include <std_string.i>
4 struct XmlDocPtrWrapper {
11 #include <libxml/globals.h>
13 #include "../c++11/xml-template.h"
15 struct XmlDocWrapper {
16 ~XmlDocWrapper() { xmlFreeDoc(ptr); }
19 typedef std::shared_ptr<XmlDocWrapper> XmlDocPtrWrapper;
21 bool is_associative_array(HashTable *ht)
23 if (ht->nNumOfElements == 0) {
26 for (unsigned i = 0; i < ht->nNumOfElements; ++i) {
27 if (!zend_hash_index_exists(ht, i)) {
34 Directive* convert_php_objects_to_directive(zval *obj)
36 switch (Z_TYPE_P(obj)) {
38 HashTable *ht = Z_ARRVAL_P(obj);
39 if (is_associative_array(ht)) {
40 std::unordered_map<std::string, Directive *> my_map;
44 ZEND_HASH_FOREACH_KEY_VAL(ht, num_key, str_key, zv) {
47 key.assign(str_key->val, str_key->len);
50 sprintf(buf, "%lu", num_key);
54 my_map.insert(make_pair(key, convert_php_objects_to_directive(zv)));
55 } ZEND_HASH_FOREACH_END();
56 return new Substitute(my_map);
58 std::vector<Directive *> subdirectives;
59 for (unsigned i = 0; i < ht->nNumOfElements; ++i) {
60 zval *data = zend_hash_index_find(ht, i);
61 subdirectives.push_back(convert_php_objects_to_directive(data));
63 return new Clone(subdirectives);
68 std::string str(Z_STRVAL_P(obj), Z_STRLEN_P(obj));
69 return new Replace(str);
73 snprintf(str, sizeof(str), "%ld", Z_LVAL_P(obj));
74 return new Replace(str);
78 snprintf(str, sizeof(str), "%f", Z_DVAL_P(obj));
79 return new Replace(str);
82 XmlDocPtrWrapper *doc;
83 if (SWIG_ConvertPtr(obj, (void **)&doc, SWIGTYPE_p_XmlDocPtrWrapper, 0) < 0 || doc == NULL) {
86 return new ReplaceInclude(xmlCopyDoc((*doc)->ptr, 1));
89 return convert_php_objects_to_directive(&Z_REF_P(obj)->val);
91 return new Replace { "" };
93 printf("WARNING: Unknown type %d!\n", Z_TYPE_P(obj));
100 XmlDocPtrWrapper XML_Template_process_file(const std::string &input_filename, Directive *root_directive, bool clean)
102 xmlDocPtr ret = process_file(input_filename, root_directive, clean);
103 delete root_directive;
104 return XmlDocPtrWrapper(new XmlDocWrapper { ret });
107 void XML_Template_process(XmlDocPtrWrapper doc, Directive *root_directive, bool clean)
109 root_directive->process(xmlDocGetRootElement(doc->ptr), clean);
110 delete root_directive;
115 int write_to_string(void *context, const char *buffer, int len)
117 std::string *str = reinterpret_cast<std::string *>(context);
118 str->append(buffer, len);
122 int close_string(void *context)
129 std::string XML_Template_convert_doc_to_string(XmlDocPtrWrapper doc, bool prettyprint)
131 xmlIndentTreeOutput = prettyprint;
133 xmlOutputBufferPtr buf = xmlOutputBufferCreateIO(write_to_string, close_string, &ret, NULL);
134 xmlSaveFormatFileTo(buf, doc->ptr, "UTF-8", prettyprint);
140 // Remove document fragments (ie. move their content up in the parent node)
141 // and combine neighboring text nodes into one.
142 void normalize_node(xmlNodePtr node)
145 for (xmlNode *child = node->children; child != NULL; child = next_child) {
146 next_child = child->next;
147 if (child->type == XML_DOCUMENT_FRAG_NODE) {
148 while (child->children != NULL) {
149 xmlAddPrevSibling(child, child->children);
152 xmlUnlinkNode(child);
157 // xmlAddPrevSibling merges adjacent text nodes, but many other things
158 // (including xmlUnlinkNode) do not, so make an extra pass.
159 for (xmlNode *child = node->children; child != NULL; child = child->next) {
160 while (child->type == XML_TEXT_NODE && (child->next != NULL && child->next->type == XML_TEXT_NODE)) {
161 xmlNode *next_child = child->next;
163 xmlChar *content = xmlNodeGetContent(next_child);
164 xmlNodeAddContent(child, content);
167 xmlUnlinkNode(next_child);
168 xmlFreeNode(next_child);
170 normalize_node(child);
174 // Clean the page of non-necessary whitespace. Leaves whitespace alone if and
175 // only if xml:space="preserve" on the element. (IOW, it doesn't parse the DTDs,
177 void clean_node(xmlNodePtr node, bool preserve_whitespace, bool aggressive)
179 if (node->type == XML_TEXT_NODE) {
180 std::string content = reinterpret_cast<const char *>(xmlNodeGetContent(node));
181 if (!preserve_whitespace) {
183 for (unsigned srcpos = 0; srcpos < content.size(); ++srcpos, ++dstpos) {
184 if (content[srcpos] == '\n' ||
185 content[srcpos] == '\t' ||
186 content[srcpos] == ' ') {
187 content[dstpos] = ' ';
189 // compress double spaces
190 if (dstpos > 0 && content[dstpos - 1] == ' ') {
194 content[dstpos] = content[srcpos];
197 content.resize(dstpos);
199 if (content.empty() || (aggressive && content == " ")) {
203 xmlNodeSetContentLen(node, reinterpret_cast<const xmlChar *>(content.data()), content.size());
206 if (node->type == XML_ELEMENT_NODE) {
207 xmlChar *space = xmlGetProp(node, reinterpret_cast<const xmlChar *>("xml:space"));
208 preserve_whitespace = (space != NULL && strcmp(reinterpret_cast<const char *>(space), "preserve") == 0);
212 for (xmlNode *child = node->children; child != NULL; child = next_child) {
213 next_child = child->next;
214 clean_node(child, preserve_whitespace, aggressive);
217 if (node->type == XML_ELEMENT_NODE && node->children == NULL) {
218 std::string tag = reinterpret_cast<const char *>(node->name);
220 // These are the only elements allowed in XHTML to be EMPTY,
221 // so insert dummy nodes to prevent the output from using
222 // the <foo/> syntax where not appropriate.
223 if (tag != "base" && tag != "meta" && tag != "link" && tag != "hr" &&
224 tag != "br" && tag != "param" && tag != "img" && tag != "area" &&
225 tag != "input" && tag != "col") {
226 xmlNode *text = xmlNewText(reinterpret_cast<const xmlChar *>(""));
227 xmlAddChild(node, text);
235 void XML_Template_clean_whitespace(XmlDocPtrWrapper doc, bool aggressive)
237 normalize_node(xmlDocGetRootElement(doc->ptr));
238 clean_node(xmlDocGetRootElement(doc->ptr), false, aggressive);
243 %typemap(in) Directive* {
244 $1 = convert_php_objects_to_directive(&$input);
247 XmlDocPtrWrapper XML_Template_process_file(const std::string &input_filename, Directive *root_directive, bool clean);
248 void XML_Template_process(XmlDocPtrWrapper doc, Directive *root_directive, bool clean);
249 void XML_Template_clean_whitespace(XmlDocPtrWrapper doc, bool aggressive);
250 std::string XML_Template_convert_doc_to_string(XmlDocPtrWrapper doc, bool prettyprint);