]> git.sesse.net Git - xml-template/blob - php7-swig/xml-template.swig
Update the PHP SWIG version to PHP 7, with various crash fixes, too.
[xml-template] / php7-swig / xml-template.swig
1 %module XML_Template_SWIG
2 %include <std_string.i>
3
4 struct XmlDocPtrWrapper {
5         ~XmlDocPtrWrapper();
6 };
7
8 %{
9
10 #include <memory>
11 #include <libxml/globals.h>
12
13 #include "../c++11/xml-template.h"
14
15 struct XmlDocWrapper {
16         ~XmlDocWrapper() { xmlFreeDoc(ptr); }
17         xmlDocPtr ptr;
18 };
19 typedef std::shared_ptr<XmlDocWrapper> XmlDocPtrWrapper;
20
21 bool is_associative_array(HashTable *ht)
22 {
23         if (ht->nNumOfElements == 0) {
24                 return true;
25         }
26         for (unsigned i = 0; i < ht->nNumOfElements; ++i) {
27                 if (!zend_hash_index_exists(ht, i)) {
28                         return true;
29                 }
30         }
31         return false;
32 }
33
34 Directive* convert_php_objects_to_directive(zval *obj)
35 {
36         switch (Z_TYPE_P(obj)) {
37         case IS_ARRAY: {
38                 HashTable *ht = Z_ARRVAL_P(obj);
39                 if (is_associative_array(ht)) {
40                         std::unordered_map<std::string, Directive *> my_map;
41                         zend_string *str_key;
42                         zval *zv;
43                         ulong num_key;
44                         ZEND_HASH_FOREACH_KEY_VAL(ht, num_key, str_key, zv) {
45                                 std::string key;
46                                 if (str_key) {
47                                         key.assign(str_key->val, str_key->len);
48                                 } else {
49                                         char buf[32];
50                                         sprintf(buf, "%lu", num_key);
51                                         key = buf;
52                                 }
53
54                                 my_map.insert(make_pair(key, convert_php_objects_to_directive(zv)));
55                         } ZEND_HASH_FOREACH_END();
56                         return new Substitute(my_map);
57                 } else {
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));
62                         }
63                         return new Clone(subdirectives);
64                 }
65                 break;
66         }
67         case IS_STRING: {
68                 std::string str(Z_STRVAL_P(obj), Z_STRLEN_P(obj));
69                 return new Replace(str);
70         }
71         case IS_LONG: {
72                 char str[256];
73                 snprintf(str, sizeof(str), "%ld", Z_LVAL_P(obj));
74                 return new Replace(str);
75         }
76         case IS_DOUBLE: {
77                 char str[256];
78                 snprintf(str, sizeof(str), "%f", Z_DVAL_P(obj));
79                 return new Replace(str);
80         }
81         case IS_RESOURCE: {
82                 XmlDocPtrWrapper *doc;
83                 if (SWIG_ConvertPtr(obj, (void **)&doc, SWIGTYPE_p_XmlDocPtrWrapper, 0) < 0 || doc == NULL) {
84                         return NULL;
85                 }
86                 return new ReplaceInclude(xmlCopyDoc((*doc)->ptr, 1));
87         }
88         case IS_REFERENCE:
89                 return convert_php_objects_to_directive(&Z_REF_P(obj)->val);
90         case IS_NULL:
91                 return new Replace { "" };
92         default:
93                 printf("WARNING: Unknown type %d!\n", Z_TYPE_P(obj));
94                 break;
95         }
96
97         return NULL;
98 }
99
100 XmlDocPtrWrapper XML_Template_process_file(const std::string &input_filename, Directive *root_directive, bool clean)
101 {
102         xmlDocPtr ret = process_file(input_filename, root_directive, clean);
103         delete root_directive;
104         return XmlDocPtrWrapper(new XmlDocWrapper { ret });
105 }
106
107 void XML_Template_process(XmlDocPtrWrapper doc, Directive *root_directive, bool clean)
108 {
109         root_directive->process(xmlDocGetRootElement(doc->ptr), clean);
110         delete root_directive;
111 }
112
113 namespace {
114
115 int write_to_string(void *context, const char *buffer, int len)
116 {
117         std::string *str = reinterpret_cast<std::string *>(context);
118         str->append(buffer, len);
119         return len;
120 }
121
122 int close_string(void *context)
123 {
124         return 0;
125 }
126
127 }  // namespace
128
129 std::string XML_Template_convert_doc_to_string(XmlDocPtrWrapper doc, bool prettyprint)
130 {
131         xmlIndentTreeOutput = prettyprint;
132         std::string ret;
133         xmlOutputBufferPtr buf = xmlOutputBufferCreateIO(write_to_string, close_string, &ret, NULL);
134         xmlSaveFormatFileTo(buf, doc->ptr, "UTF-8", prettyprint);
135         return ret;
136 }
137
138 namespace {
139
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)
143 {
144         xmlNode *next_child;
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);
150                         }
151
152                         xmlUnlinkNode(child);
153                         xmlFreeNode(child);
154                 }
155         }
156
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;
162
163                         xmlChar *content = xmlNodeGetContent(next_child);
164                         xmlNodeAddContent(child, content);
165                         xmlFree(content);
166
167                         xmlUnlinkNode(next_child);
168                         xmlFreeNode(next_child);
169                 }
170                 normalize_node(child);
171         }
172 }
173
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,
176 // nor the CSS.)
177 void clean_node(xmlNodePtr node, bool preserve_whitespace, bool aggressive)
178 {
179         if (node->type == XML_TEXT_NODE) {
180                 std::string content = reinterpret_cast<const char *>(xmlNodeGetContent(node));
181                 if (!preserve_whitespace) {
182                         unsigned dstpos = 0;
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] = ' ';
188
189                                         // compress double spaces
190                                         if (dstpos > 0 && content[dstpos - 1] == ' ') {
191                                                 --dstpos;
192                                         }
193                                 } else {
194                                         content[dstpos] = content[srcpos];
195                                 }
196                         }
197                         content.resize(dstpos);
198                 }
199                 if (content.empty() || (aggressive && content == " ")) {
200                         xmlUnlinkNode(node);
201                         xmlFreeNode(node);
202                 } else {
203                         xmlNodeSetContentLen(node, reinterpret_cast<const xmlChar *>(content.data()), content.size());
204                 }
205         } else {
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);
209                 }
210
211                 xmlNode *next_child;
212                 for (xmlNode *child = node->children; child != NULL; child = next_child) {
213                         next_child = child->next;
214                         clean_node(child, preserve_whitespace, aggressive);
215                 }
216
217                 if (node->type == XML_ELEMENT_NODE && node->children == NULL) {
218                         std::string tag = reinterpret_cast<const char *>(node->name);
219
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);
228                         }
229                 }
230         }
231 }
232
233 }  // namespace
234
235 void XML_Template_clean_whitespace(XmlDocPtrWrapper doc, bool aggressive)
236 {
237         normalize_node(xmlDocGetRootElement(doc->ptr));
238         clean_node(xmlDocGetRootElement(doc->ptr), false, aggressive);
239 }
240
241 %}
242
243 %typemap(in) Directive* {
244         $1 = convert_php_objects_to_directive(&$input);
245 }
246
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);
251