Overview

Packages

  • Xml
    • Wbxml

Classes

  • Horde_Xml_Wbxml
  • Horde_Xml_Wbxml_ContentHandler
  • Horde_Xml_Wbxml_Decoder
  • Horde_Xml_Wbxml_Dtd
  • Horde_Xml_Wbxml_Dtd_SyncMl
  • Horde_Xml_Wbxml_Dtd_SyncMlDevInf
  • Horde_Xml_Wbxml_Dtd_SyncMlMetInf
  • Horde_Xml_Wbxml_DtdManager
  • Horde_Xml_Wbxml_Encoder
  • Horde_Xml_Wbxml_Exception
  • Horde_Xml_Wbxml_HashTable
  • Horde_Xml_Wbxml_LifoQueue
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * From Binary XML Content Format Specification Version 1.3, 25 July 2001
  4:  * found at http://www.wapforum.org
  5:  *
  6:  * Copyright 2003-2012 Horde LLC (http://www.horde.org/)
  7:  *
  8:  * See the enclosed file COPYING for license information (LGPL). If you
  9:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
 10:  *
 11:  * @author  Anthony Mills <amills@pyramid6.com>
 12:  * @package Xml_Wbxml
 13:  */
 14: class Horde_Xml_Wbxml_Encoder extends Horde_Xml_Wbxml_ContentHandler
 15: {
 16:     protected $_strings = array();
 17: 
 18:     protected $_stringTable;
 19: 
 20:     protected $_hasWrittenHeader = false;
 21: 
 22:     protected $_dtd;
 23: 
 24:     protected $_output = '';
 25: 
 26:     protected $_uris = array();
 27: 
 28:     protected $_uriNums = array();
 29: 
 30:     protected $_currentURI;
 31: 
 32:     protected $_subParser = null;
 33:     protected $_subParserStack = 0;
 34: 
 35:     /**
 36:      * The XML parser.
 37:      *
 38:      * @var resource
 39:      */
 40:     protected $_parser;
 41: 
 42:     /**
 43:      * The DTD Manager.
 44:      *
 45:      * @var Horde_Xml_Wbxml_DtdManager
 46:      */
 47:     protected $_dtdManager;
 48: 
 49:     /**
 50:      * Constructor.
 51:      */
 52:     public function Horde_Xml_Wbxml_Encoder()
 53:     {
 54:         $this->_dtdManager = new Horde_Xml_Wbxml_DtdManager();
 55:         $this->_stringTable = new Horde_Xml_Wbxml_HashTable();
 56:     }
 57: 
 58:     /**
 59:      * Take the input $xml and turn it into WBXML. This is _not_ the
 60:      * intended way of using this class. It is derived from
 61:      * Contenthandler and one should use it as a ContentHandler and
 62:      * produce the XML-structure with startElement(), endElement(),
 63:      * and characters().
 64:      *
 65:      * @throws Horde_Xml_Wbxml_Exception
 66:      */
 67:     public function encode($xml)
 68:     {
 69:         // Create the XML parser and set method references.
 70:         $this->_parser = xml_parser_create_ns($this->_charset);
 71:         xml_set_object($this->_parser, $this);
 72:         xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false);
 73:         xml_set_element_handler($this->_parser, '_startElement', '_endElement');
 74:         xml_set_character_data_handler($this->_parser, '_characters');
 75:         xml_set_processing_instruction_handler($this->_parser, '');
 76:         xml_set_external_entity_ref_handler($this->_parser, '');
 77: 
 78:         if (!xml_parse($this->_parser, $xml)) {
 79:             throw new Horde_Xml_Wbxml_Exception(
 80:                 sprintf('XML error: %s at line %d',
 81:                         xml_error_string(xml_get_error_code($this->_parser)),
 82:                         xml_get_current_line_number($this->_parser)));
 83:         }
 84: 
 85:         xml_parser_free($this->_parser);
 86: 
 87:         return $this->_output;
 88:     }
 89: 
 90:     /**
 91:      * This will write the correct headers.
 92:      *
 93:      * @throws Horde_Xml_Wbxml_Exception
 94:      */
 95:     public function writeHeader($uri)
 96:     {
 97:         // @todo: this is a hack!
 98:         if ($this->_wbxmlVersion == 2 && !preg_match('/1\.2$/', $uri)) {
 99:             $uri .= '1.2';
100:         }
101:         if ($this->_wbxmlVersion == 1 && !preg_match('/1\.1$/', $uri)) {
102:             $uri .= '1.1';
103:         }
104:         if ($this->_wbxmlVersion == 0 && !preg_match('/1\.0$/', $uri)) {
105:             $uri .= '1.0';
106:         }
107: 
108:         $this->_dtd = $this->_dtdManager->getInstanceURI($uri);
109:         if (!$this->_dtd) {
110:             throw new Horde_Xml_Wbxml_Exception('Unable to find dtd for ' . $uri);
111:         }
112:         $dpiString = $this->_dtd->getDPI();
113: 
114:         // Set Version Number from Section 5.4
115:         // version = u_int8
116:         // currently 1, 2 or 3
117:         $this->writeVersionNumber($this->_wbxmlVersion);
118: 
119:         // Set Document Public Idetifier from Section 5.5
120:         // publicid = mb_u_int32 | ( zero index )
121:         // zero = u_int8
122:         // containing the value zero (0)
123:         // The actual DPI is determined after the String Table is read.
124:         $this->writeDocumentPublicIdentifier($dpiString, $this->_strings);
125: 
126:         // Set Charset from 5.6
127:         // charset = mb_u_int32
128:         $this->writeCharset($this->_charset);
129: 
130:         // Set String Table from 5.7
131:         // strb1 = length *byte
132:         $this->writeStringTable($this->_strings, $this->_charset, $this->_stringTable);
133: 
134:         $this->_currentURI = $uri;
135: 
136:         $this->_hasWrittenHeader = true;
137:     }
138: 
139:     public function writeVersionNumber($version)
140:     {
141:         $this->_output .= chr($version);
142:     }
143: 
144:     public function writeDocumentPublicIdentifier($dpiString, &$strings)
145:     {
146:         $i = 0;
147: 
148:         // The OMA test suite doesn't like DPI as integer code.
149:         // So don't try lookup and always send full DPI string.
150:         // $i = Horde_Xml_Wbxml::getDPIInt($dpiString);
151: 
152:         if ($i == 0) {
153:             $strings[0] = $dpiString;
154:             $this->_output .= chr(0);
155:             $this->_output .= chr(0);
156:         } else {
157:             Horde_Xml_Wbxml::intToMBUInt32($this->_output, $i);
158:         }
159:     }
160: 
161:     /**
162:      * @throws Horde_Xml_Wbxml_Exception
163:      */
164:     public function writeCharset($charset)
165:     {
166:         $cs = Horde_Xml_Wbxml::getCharsetInt($charset);
167: 
168:         if ($cs == 0) {
169:             throw new Horde_Xml_Wbxml_Exception('Unsupported Charset: ' . $charset);
170:         } else {
171:             Horde_Xml_Wbxml::intToMBUInt32($this->_output, $cs);
172:         }
173:     }
174: 
175:     public function writeStringTable($strings, $charset, $stringTable)
176:     {
177:         $stringBytes = array();
178:         $count = 0;
179:         foreach ($strings as $str) {
180:             $bytes = $this->_getBytes($str, $charset);
181:             $stringBytes = array_merge($stringBytes, $bytes);
182:             $nullLength = $this->_addNullByte($bytes);
183:             $this->_stringTable->set($str, $count);
184:             $count += count($bytes) + $nullLength;
185:         }
186: 
187:         Horde_Xml_Wbxml::intToMBUInt32($this->_output, count($stringBytes));
188:         $this->_output .= implode('', $stringBytes);
189:     }
190: 
191:     public function writeString($str, $cs)
192:     {
193:         $bytes = $this->_getBytes($str, $cs);
194:         $this->_output .= implode('', $bytes);
195:         $this->writeNull($cs);
196:     }
197: 
198:     public function writeNull($charset)
199:     {
200:         $this->_output .= chr(0);
201:         return 1;
202:     }
203: 
204:     protected function _addNullByte(&$bytes)
205:     {
206:         $bytes[] = chr(0);
207:         return 1;
208:     }
209: 
210:     protected function _getBytes($string, $cs)
211:     {
212:         $nbytes = strlen($string);
213: 
214:         $bytes = array();
215:         for ($i = 0; $i < $nbytes; $i++) {
216:             $bytes[] = $string{$i};
217:         }
218: 
219:         return $bytes;
220:     }
221: 
222:     protected function _splitURI($tag)
223:     {
224:         $parts = explode(':', $tag);
225:         $name = array_pop($parts);
226:         $uri = implode(':', $parts);
227:         return array($uri, $name);
228:     }
229: 
230:     /**
231:      * @throws Horde_Xml_Wbxml_Exception
232:      */
233:     public function startElement($uri, $name, $attributes = array())
234:     {
235:         if ($this->_subParser == null) {
236:             if (!$this->_hasWrittenHeader) {
237:                 $this->writeHeader($uri);
238:             }
239:             if ($this->_currentURI != $uri) {
240:                 $this->changecodepage($uri);
241:                 $this->_currentURI = $uri;
242:             }
243:             if ($this->_subParser == null) {
244:                 $this->writeTag($name, $attributes, true, $this->_charset);
245:             } else {
246:                 $this->_subParser->startElement($uri, $name, $attributes);
247:             }
248:         } else {
249:             $this->_subParserStack++;
250:             $this->_subParser->startElement($uri, $name, $attributes);
251:         }
252:     }
253: 
254:     protected function _startElement($parser, $tag, $attributes)
255:     {
256:         list($uri, $name) = $this->_splitURI($tag);
257:         if (in_array(Horde_String::lower($uri), array('syncml:metinf', 'syncml:devinf'))) {
258:             $uri .= '1.' . $this->getVersion();
259:         }
260:         $this->startElement($uri, $name, $attributes);
261:     }
262: 
263:     public function opaque($o)
264:     {
265:         $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_OPAQUE);
266:         Horde_Xml_Wbxml::intToMBUInt32($this->_output, strlen($o));
267:         $this->_output .= $o;
268:     }
269: 
270:     public function characters($chars)
271:     {
272:         $chars = trim($chars);
273: 
274:         if (strlen($chars)) {
275:             /* We definitely don't want any whitespace. */
276:             if ($this->_subParser == null) {
277:                 $i = $this->_stringTable->get($chars);
278:                 if ($i != null) {
279:                     $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_STR_T);
280:                     Horde_Xml_Wbxml::intToMBUInt32($this->_output, $i);
281:                 } else {
282:                     $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_STR_I);
283:                     $this->writeString($chars, $this->_charset);
284:                 }
285:             } else {
286:                 $this->_subParser->characters($chars);
287:             }
288:         }
289:     }
290: 
291:     protected function _characters($parser, $chars)
292:     {
293:         $this->characters($chars);
294:     }
295: 
296:     /**
297:      * @throws Horde_Xml_Wbxml_Exception
298:      */
299:     public function writeTag($name, $attrs, $hasContent, $cs)
300:     {
301:         if ($attrs != null && !count($attrs)) {
302:             $attrs = null;
303:         }
304: 
305:         $t = $this->_dtd->toTagInt($name);
306:         if ($t == -1) {
307:             $i = $this->_stringTable->get($name);
308:             if ($i == null) {
309:                 throw new Horde_Xml_Wbxml_Exception($name . ' is not found in String Table or DTD');
310:             } else {
311:                 if ($attrs == null && !$hasContent) {
312:                     $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_LITERAL);
313:                 } elseif ($attrs == null && $hasContent) {
314:                     $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_LITERAL_A);
315:                 } elseif ($attrs != null && $hasContent) {
316:                     $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_LITERAL_C);
317:                 } elseif ($attrs != null && !$hasContent) {
318:                     $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_LITERAL_AC);
319:                 }
320: 
321:                 Horde_Xml_Wbxml::intToMBUInt32($this->_output, $i);
322:             }
323:         } else {
324:             if ($attrs == null && !$hasContent) {
325:                 $this->_output .= chr($t);
326:             } elseif ($attrs == null && $hasContent) {
327:                 $this->_output .= chr($t | 64);
328:             } elseif ($attrs != null && $hasContent) {
329:                 $this->_output .= chr($t | 128);
330:             } elseif ($attrs != null && !$hasContent) {
331:                 $this->_output .= chr($t | 192);
332:             }
333:         }
334: 
335:         if ($attrs != null && is_array($attrs) && count($attrs) > 0) {
336:             $this->writeAttributes($attrs, $cs);
337:         }
338:     }
339: 
340:     public function writeAttributes($attrs, $cs)
341:     {
342:         foreach ($attrs as $name => $value) {
343:             $this->writeAttribute($name, $value, $cs);
344:         }
345: 
346:         $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_END);
347:     }
348: 
349:     /**
350:      * @throws Horde_Xml_Wbxml_Exception
351:      */
352:     public function writeAttribute($name, $value, $cs)
353:     {
354:         $a = $this->_dtd->toAttribute($name);
355:         if ($a == -1) {
356:             $i = $this->_stringTable->get($name);
357:             if ($i == null) {
358:                 throw new Horde_Xml_Wbxml_Exception($name . ' is not found in String Table or DTD');
359:             } else {
360:                 $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_LITERAL);
361:                 Horde_Xml_Wbxml::intToMBUInt32($this->_output, $i);
362:             }
363:         } else {
364:             $this->_output .= $a;
365:         }
366: 
367:         $i = $this->_stringTable->get($name);
368:         if ($i != null) {
369:             $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_STR_T);
370:             Horde_Xml_Wbxml::intToMBUInt32($this->_output, $i);
371:         } else {
372:             $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_STR_I);
373:             $this->writeString($value, $cs);
374:         }
375:     }
376: 
377:     public function endElement($uri, $name)
378:     {
379:         if ($this->_subParser == null) {
380:             $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_END);
381:         } else {
382:             $this->_subParser->endElement($uri, $name);
383:             $this->_subParserStack--;
384: 
385:             if ($this->_subParserStack == 0) {
386:                 $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_OPAQUE);
387: 
388:                 Horde_Xml_Wbxml::intToMBUInt32($this->_output,
389:                                          strlen($this->_subParser->getOutput()));
390:                 $this->_output .= $this->_subParser->getOutput();
391: 
392:                 $this->_subParser = null;
393:             }
394:         }
395:     }
396: 
397:     protected function _endElement($parser, $tag)
398:     {
399:         list($uri, $name) = $this->_splitURI($tag);
400:         $this->endElement($uri, $name);
401:     }
402: 
403:     public function changecodepage($uri)
404:     {
405:         // @todo: this is a hack!
406:         if ($this->_dtd->getVersion() == 2 && !preg_match('/1\.2$/', $uri)) {
407:             $uri .= '1.2';
408:         }
409:         if ($this->_dtd->getVersion() == 1 && !preg_match('/1\.1$/', $uri)) {
410:             $uri .= '1.1';
411:         }
412:         if ($this->_dtd->getVersion() == 0 && !preg_match('/1\.0$/', $uri)) {
413:             $uri .= '1.0';
414:         }
415: 
416:         $cp = $this->_dtd->toCodePageURI($uri);
417:         if (strlen($cp)) {
418:             $this->_dtd = $this->_dtdManager->getInstanceURI($uri);
419:             if (!$this->_dtd) {
420:                 throw new Horde_Xml_Wbxml_Exception('Unable to find dtd for ' . $uri);
421:             }
422:             $this->_output .= chr(Horde_Xml_Wbxml::GLOBAL_TOKEN_SWITCH_PAGE);
423:             $this->_output .= chr($cp);
424:         } else {
425:             $this->_subParser = new Horde_Xml_Wbxml_Encoder(true);
426:             $this->_subParserStack = 1;
427:         }
428:     }
429: 
430:     /**
431:      * Getter for property output.
432:      */
433:     public function getOutput()
434:     {
435:         return $this->_output;
436:     }
437: 
438:     public function getOutputSize()
439:     {
440:         return strlen($this->_output);
441:     }
442: }
443: 
API documentation generated by ApiGen